@agent-native/core-corpus 0.199.0 → 0.200.0-nightly-20261001213023
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +4 -4
- package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +200 -5
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +108 -16
- package/corpus/templates/assets/_gitignore +23 -23
- package/corpus/templates/chat/app/components/chat/ChatRouteContent.tsx +27 -10
- package/corpus/templates/clips/actions/create-recording.ts +2 -2
- package/corpus/templates/clips/actions/import-loom-recording.ts +5 -3
- package/corpus/templates/clips/actions/lib/loom-import-job.ts +113 -29
- package/corpus/templates/clips/changelog/2026-10-01-loom-imports-no-longer-stay-stuck-if-their-upload-worker-sto.md +6 -0
- package/corpus/templates/clips/server/lib/upload-lease.ts +39 -2
- package/corpus/templates/content/agent-native.app-skill.json +1 -1
- package/corpus/templates/content/app/hooks/use-documents.ts +73 -2
- package/corpus/templates/content/changelog/2026-09-30-recent-page-icons-update-immediately-when-changed-or-removed.md +6 -0
- package/corpus/templates/design/actions/generate-home-suggestions.ts +39 -4
- package/corpus/templates/design/agent-native.app-skill.json +1 -1
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +4 -2
- package/corpus/templates/design/changelog/2026-10-01-selecting-a-layer-keeps-its-indentation-and-icon-visible-in-.md +6 -0
- package/corpus/templates/design/changelog/2026-10-01-the-home-page-no-longer-fails-when-ai-suggestions-are-unavai.md +6 -0
- package/corpus/templates/mail/.env.example +34 -34
- package/corpus/templates/plan/app/pages/PlansPage.tsx +25 -4
- package/corpus/templates/plan/changelog/2026-10-01-plan-edits-no-longer-fail-with-stale-revision-conflicts-during-consecutive-saves.md +6 -0
- package/corpus/templates/slides/.ignore +2 -2
- package/corpus/templates/slides/actions/generate-home-suggestions.ts +39 -4
- package/corpus/templates/slides/agent-native.app-skill.json +1 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +67 -16
- package/corpus/templates/slides/app/pages/Index.tsx +56 -31
- package/corpus/templates/slides/changelog/2026-10-01-images-can-be-removed-from-chat-without-opening-their-previe.md +6 -0
- package/corpus/templates/slides/changelog/2026-10-01-slide-decks-persist-after-generation-setup-is-ready.md +6 -0
- package/package.json +1 -1
package/corpus/README.md
CHANGED
|
@@ -584,7 +584,7 @@
|
|
|
584
584
|
"chartType": "line",
|
|
585
585
|
"source": "first-party",
|
|
586
586
|
"width": 3,
|
|
587
|
-
"sql": "WITH base AS (\n SELECT NULLIF(user_key, '') AS user_key, event_date AS event_date, user_id\n FROM analytics_events\n WHERE ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND ((event_name IN ('session status', 'session_status') AND signed_in = 'true') OR (event_name = 'app_entered' AND NULLIF(user_id, '') IS NOT NULL)) AND NULLIF(user_key, '') IS NOT NULL AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) <> 'docs' AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides') AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) <> 'www' AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(user_id, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(user_id, '')) LIKE '%@builder.io'))\n AND event_date >= CASE WHEN '{{timeRange}}' = 'custom' THEN to_char(NULLIF('{{timeRangeStart}}', '')::date - INTERVAL '6 days', 'YYYY-MM-DD') ELSE to_char(CURRENT_DATE - INTERVAL '371 days', 'YYYY-MM-DD') END\n AND event_date <= CASE WHEN '{{timeRange}}' = 'custom' THEN to_char(LEAST(NULLIF('{{timeRangeEnd}}', '')::date + INTERVAL '14 days', CURRENT_DATE), 'YYYY-MM-DD') ELSE to_char(CURRENT_DATE, 'YYYY-MM-DD') END\n), cohort_history AS (\n SELECT NULLIF(user_key, '') AS user_key, event_date AS event_date, user_id\n FROM analytics_events\n WHERE ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND '{{timeRange}}' = 'custom'\n AND ((event_name IN ('session status', 'session_status') AND signed_in = 'true') OR (event_name = 'app_entered' AND NULLIF(user_id, '') IS NOT NULL)) AND NULLIF(user_key, '') IS NOT NULL AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) <> 'docs' AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides') AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) <> 'www' AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(user_id, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(user_id, '')) LIKE '%@builder.io'))\n AND event_date >= to_char(NULLIF('{{timeRangeStart}}', '')::date - INTERVAL '371 days', 'YYYY-MM-DD')\n AND event_date < to_char(NULLIF('{{timeRangeStart}}', '')::date - INTERVAL '6 days', 'YYYY-MM-DD')\n AND event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD')\n), first_seen AS (\n SELECT user_key, MIN(event_date) AS cohort_date\n FROM (\n SELECT user_key, event_date FROM base\n UNION ALL\n SELECT user_key, event_date FROM cohort_history\n ) activity\n GROUP BY user_key\n), date_spine_bounds AS (\n SELECT\n (CASE WHEN '{{timeRange}}' = 'custom' THEN NULLIF('{{timeRangeStart}}', '')::date ELSE CURRENT_DATE - (CASE '{{timeRange}}' WHEN '7d' THEN 7 WHEN '30d' THEN 30 WHEN '90d' THEN 90 WHEN '180d' THEN 180 WHEN '365d' THEN 365 ELSE 365 END) END)::timestamp AS start_date,\n (CASE WHEN '{{timeRange}}' = 'custom' THEN LEAST(NULLIF('{{timeRangeEnd}}', '')::date, CURRENT_DATE) ELSE CURRENT_DATE END)::timestamp AS end_date\n), anchor_dates AS (\n SELECT to_char(anchor_date::date, 'YYYY-MM-DD') AS date\n FROM date_spine_bounds\n CROSS JOIN LATERAL pg_catalog.generate_series(\n GREATEST(date_spine_bounds.start_date, date_spine_bounds.end_date - INTERVAL '3659 days'),\n date_spine_bounds.end_date,\n INTERVAL '1 day'\n ) AS anchor_date\n), cohort_windows AS (\n SELECT a.date, f.user_key, f.cohort_date\n FROM anchor_dates a JOIN first_seen f ON f.cohort_date >= to_char(a.date::date - INTERVAL '6 days', 'YYYY-MM-DD') AND f.cohort_date <= a.date\n), cohort_sizes AS (\n SELECT date, COUNT(DISTINCT user_key) AS users FROM cohort_windows GROUP BY date\n), periods AS (\n SELECT '1-7d return' AS period, to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD') AS mature_through\n UNION ALL SELECT '7-14d return' AS period, to_char(CURRENT_DATE - INTERVAL '14 days', 'YYYY-MM-DD') AS mature_through\n), retained AS (\n SELECT cw.date, '1-7d return' AS period, COUNT(DISTINCT cw.user_key) AS retained\n FROM cohort_windows cw JOIN base b ON b.user_key = cw.user_key AND b.event_date > cw.cohort_date AND b.event_date <= to_char(cw.cohort_date::date + INTERVAL '7 days', 'YYYY-MM-DD')\n GROUP BY cw.date\n UNION ALL\n SELECT cw.date, '7-14d return' AS period, COUNT(DISTINCT cw.user_key) AS retained\n FROM cohort_windows cw JOIN base b ON b.user_key = cw.user_key AND b.event_date >= to_char(cw.cohort_date::date + INTERVAL '7 days', 'YYYY-MM-DD') AND b.event_date <= to_char(cw.cohort_date::date + INTERVAL '14 days', 'YYYY-MM-DD')\n GROUP BY cw.date\n)\nSELECT a.date, p.period,\n CASE WHEN a.date <= p.mature_through AND cs.users >= 5 THEN COALESCE(r.retained, 0) ELSE NULL END AS retained_users,\n COALESCE(cs.users, 0) AS cohort_users,\n CASE WHEN a.date <= p.mature_through AND cs.users >= 5 THEN COALESCE(r.retained, 0)::float / NULLIF(cs.users, 0) ELSE NULL END AS rate\nFROM anchor_dates a CROSS JOIN periods p\nLEFT JOIN cohort_sizes cs ON cs.date = a.date\nLEFT JOIN retained r ON r.date = a.date AND r.period = p.period\nORDER BY a.date, p.period",
|
|
587
|
+
"sql": "WITH digits AS (SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9), offsets AS (SELECT ones.n + tens.n * 10 + hundreds.n * 100 + thousands.n * 1000 AS n FROM digits ones CROSS JOIN digits tens CROSS JOIN digits hundreds CROSS JOIN digits thousands WHERE thousands.n < 4 AND ones.n + tens.n * 10 + hundreds.n * 100 + thousands.n * 1000 <= 3659), date_spine_bounds AS (\n SELECT\n GREATEST(range_bounds.start_date, (range_bounds.end_date - INTERVAL '3659 days')::date) AS start_date,\n range_bounds.end_date\n FROM (\n SELECT\n (CASE WHEN '{{timeRange}}' = 'custom' THEN NULLIF('{{timeRangeStart}}', '')::date ELSE CURRENT_DATE - (CASE '{{timeRange}}' WHEN '7d' THEN 7 WHEN '30d' THEN 30 WHEN '90d' THEN 90 WHEN '180d' THEN 180 WHEN '365d' THEN 365 ELSE 365 END) END)::date AS start_date,\n (CASE WHEN '{{timeRange}}' = 'custom' THEN LEAST(NULLIF('{{timeRangeEnd}}', '')::date, CURRENT_DATE) ELSE CURRENT_DATE END)::date AS end_date\n ) AS range_bounds\n), anchor_dates AS (\n SELECT to_char(date_spine_bounds.start_date + offsets.n, 'YYYY-MM-DD') AS date\n FROM date_spine_bounds\n CROSS JOIN offsets\n WHERE date_spine_bounds.start_date + offsets.n <= date_spine_bounds.end_date\n), identity_emails AS (\n SELECT user_key, email\n FROM (\n SELECT NULLIF(properties::jsonb ->> 'auth_user_id', '') AS user_key,\n lower(NULLIF(user_id, '')) AS email,\n ROW_NUMBER() OVER (PARTITION BY NULLIF(properties::jsonb ->> 'auth_user_id', '') ORDER BY timestamp DESC, user_id DESC) AS email_rank\n FROM analytics_events\n CROSS JOIN date_spine_bounds\n WHERE NULLIF(properties::jsonb ->> 'auth_user_id', '') IS NOT NULL\n AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n AND NULLIF(user_id, '') IS NOT NULL\n AND (\n ('{{timeRange}}' = 'custom' AND event_date >= to_char((date_spine_bounds.start_date - INTERVAL '371 days')::date, 'YYYY-MM-DD'))\n OR ('{{timeRange}}' <> 'custom' AND event_date >= to_char(CURRENT_DATE - INTERVAL '371 days', 'YYYY-MM-DD'))\n )\n AND event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD')\n ) email_candidates\n WHERE email_rank = 1\n), base AS (\n SELECT NULLIF(properties::jsonb ->> 'auth_user_id', '') AS user_key, event_date AS event_date\n FROM analytics_events\n CROSS JOIN date_spine_bounds\n LEFT JOIN identity_emails ON identity_emails.user_key = NULLIF(properties::jsonb ->> 'auth_user_id', '')\n WHERE NULLIF(properties::jsonb ->> 'auth_user_id', '') IS NOT NULL AND (\n (event_name IN ('action_completed', 'core_action_completed')\n AND COALESCE(properties::jsonb ->> 'success', 'true') = 'true'\n AND NULLIF(properties::jsonb ->> 'output_id', '') IS NOT NULL)\n OR event_name IN ('generation_completed', 'design_created', 'plan_created', 'recording_ready')\n OR event_name = 'run_started'\n OR (event_name = 'app.first_action' AND properties::jsonb ->> 'action' = 'chat_submit')\n OR (event_name = 'core_action_started' AND properties::jsonb ->> 'action_name' = 'chat_submit')\n) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) <> 'docs' AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides') AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) <> 'www' AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND COALESCE(identity_emails.email, '') NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND COALESCE(identity_emails.email, '') LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}'))\n AND date_spine_bounds.start_date <= date_spine_bounds.end_date\n AND (\n ('{{timeRange}}' = 'custom' AND event_date >= to_char((date_spine_bounds.start_date - INTERVAL '6 days')::date, 'YYYY-MM-DD'))\n OR ('{{timeRange}}' <> 'custom' AND event_date >= to_char(CURRENT_DATE - INTERVAL '371 days', 'YYYY-MM-DD'))\n )\n AND event_date <= to_char(LEAST(date_spine_bounds.end_date + INTERVAL '14 days', CURRENT_DATE)::date, 'YYYY-MM-DD')\n), cohort_history AS (\n SELECT NULLIF(properties::jsonb ->> 'auth_user_id', '') AS user_key, event_date AS event_date\n FROM analytics_events\n CROSS JOIN date_spine_bounds\n LEFT JOIN identity_emails ON identity_emails.user_key = NULLIF(properties::jsonb ->> 'auth_user_id', '')\n WHERE '{{timeRange}}' = 'custom'\n AND NULLIF(properties::jsonb ->> 'auth_user_id', '') IS NOT NULL AND (\n (event_name IN ('action_completed', 'core_action_completed')\n AND COALESCE(properties::jsonb ->> 'success', 'true') = 'true'\n AND NULLIF(properties::jsonb ->> 'output_id', '') IS NOT NULL)\n OR event_name IN ('generation_completed', 'design_created', 'plan_created', 'recording_ready')\n OR event_name = 'run_started'\n OR (event_name = 'app.first_action' AND properties::jsonb ->> 'action' = 'chat_submit')\n OR (event_name = 'core_action_started' AND properties::jsonb ->> 'action_name' = 'chat_submit')\n) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) <> 'docs' AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides') AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) <> 'www' AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND COALESCE(identity_emails.email, '') NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND COALESCE(identity_emails.email, '') LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}'))\n AND date_spine_bounds.start_date <= date_spine_bounds.end_date\n AND event_date >= to_char((date_spine_bounds.start_date - INTERVAL '371 days')::date, 'YYYY-MM-DD')\n AND event_date < to_char((date_spine_bounds.start_date - INTERVAL '6 days')::date, 'YYYY-MM-DD')\n AND event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD')\n), first_seen AS (\n SELECT user_key, MIN(event_date) AS cohort_date\n FROM (\n SELECT user_key, event_date FROM base\n UNION ALL\n SELECT user_key, event_date FROM cohort_history\n ) activity\n GROUP BY user_key\n), cohort_windows AS (\n SELECT a.date, f.user_key, f.cohort_date\n FROM anchor_dates a JOIN first_seen f ON f.cohort_date >= to_char(a.date::date - INTERVAL '6 days', 'YYYY-MM-DD') AND f.cohort_date <= a.date\n), cohort_sizes AS (\n SELECT date, COUNT(DISTINCT user_key) AS users FROM cohort_windows GROUP BY date\n), periods AS (\n SELECT '1-7d return' AS period, to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD') AS mature_through\n UNION ALL SELECT '7-14d return' AS period, to_char(CURRENT_DATE - INTERVAL '14 days', 'YYYY-MM-DD') AS mature_through\n), retained AS (\n SELECT cw.date, '1-7d return' AS period, COUNT(DISTINCT cw.user_key) AS retained\n FROM cohort_windows cw JOIN base b ON b.user_key = cw.user_key AND b.event_date > cw.cohort_date AND b.event_date <= to_char(cw.cohort_date::date + INTERVAL '7 days', 'YYYY-MM-DD')\n GROUP BY cw.date\n UNION ALL\n SELECT cw.date, '7-14d return' AS period, COUNT(DISTINCT cw.user_key) AS retained\n FROM cohort_windows cw JOIN base b ON b.user_key = cw.user_key AND b.event_date >= to_char(cw.cohort_date::date + INTERVAL '7 days', 'YYYY-MM-DD') AND b.event_date <= to_char(cw.cohort_date::date + INTERVAL '14 days', 'YYYY-MM-DD')\n GROUP BY cw.date\n)\nSELECT a.date, p.period,\n CASE WHEN a.date <= p.mature_through AND cs.users >= 5 THEN COALESCE(r.retained, 0) ELSE NULL END AS retained_users,\n COALESCE(cs.users, 0) AS cohort_users,\n CASE WHEN a.date <= p.mature_through AND cs.users >= 5 THEN COALESCE(r.retained, 0)::float / NULLIF(cs.users, 0) ELSE NULL END AS rate\nFROM anchor_dates a CROSS JOIN periods p\nLEFT JOIN cohort_sizes cs ON cs.date = a.date\nLEFT JOIN retained r ON r.date = a.date AND r.period = p.period\nORDER BY a.date, p.period",
|
|
588
588
|
"config": {
|
|
589
589
|
"xKey": "date",
|
|
590
590
|
"yKey": "rate",
|
|
@@ -604,7 +604,7 @@
|
|
|
604
604
|
"chartType": "bar",
|
|
605
605
|
"source": "first-party",
|
|
606
606
|
"width": 2,
|
|
607
|
-
"sql": "WITH
|
|
607
|
+
"sql": "WITH identity_emails AS (\n SELECT user_key, email\n FROM (\n SELECT NULLIF(properties::jsonb ->> 'auth_user_id', '') AS user_key,\n lower(NULLIF(user_id, '')) AS email,\n ROW_NUMBER() OVER (PARTITION BY NULLIF(properties::jsonb ->> 'auth_user_id', '') ORDER BY timestamp DESC, user_id DESC) AS email_rank\n FROM analytics_events\n WHERE NULLIF(properties::jsonb ->> 'auth_user_id', '') IS NOT NULL\n AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n AND NULLIF(user_id, '') IS NOT NULL\n AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')\n AND event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD')\n ) email_candidates\n WHERE email_rank = 1\n), base AS (SELECT NULLIF(properties::jsonb ->> 'auth_user_id', '') AS user_key, COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown') AS template, event_date AS event_date FROM analytics_events LEFT JOIN identity_emails ON identity_emails.user_key = NULLIF(properties::jsonb ->> 'auth_user_id', '') WHERE NULLIF(properties::jsonb ->> 'auth_user_id', '') IS NOT NULL AND (\n (event_name IN ('action_completed', 'core_action_completed')\n AND COALESCE(properties::jsonb ->> 'success', 'true') = 'true'\n AND NULLIF(properties::jsonb ->> 'output_id', '') IS NOT NULL)\n OR event_name IN ('generation_completed', 'design_created', 'plan_created', 'recording_ready')\n OR event_name = 'run_started'\n OR (event_name = 'app.first_action' AND properties::jsonb ->> 'action' = 'chat_submit')\n OR (event_name = 'core_action_started' AND properties::jsonb ->> 'action_name' = 'chat_submit')\n) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND COALESCE(identity_emails.email, '') NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND COALESCE(identity_emails.email, '') LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown') <> 'unknown' AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) <> 'docs' AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides') AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) <> 'www' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD') GROUP BY 1, 2, 3), observed AS (SELECT user_key, event_date, FIRST_VALUE(template) OVER (PARTITION BY user_key ORDER BY event_date, template) AS starting_template, MIN(event_date) OVER (PARTITION BY user_key ORDER BY event_date, template) AS cohort_date FROM base), cohorts AS (SELECT user_key, starting_template AS template, cohort_date, MAX(CASE WHEN event_date > cohort_date AND event_date <= to_char(cohort_date::date + INTERVAL '7 days', 'YYYY-MM-DD') THEN 1 ELSE 0 END) AS retained FROM observed WHERE cohort_date <= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD') AND (cohort_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND cohort_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND cohort_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND cohort_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND cohort_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND cohort_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND cohort_date >= '{{timeRangeStart}}' AND cohort_date <= '{{timeRangeEnd}}'))) GROUP BY user_key, starting_template, cohort_date) SELECT template, SUM(retained) AS retained_users, COUNT(*) AS cohort_users, SUM(retained)::float / NULLIF(COUNT(*), 0) AS rate FROM cohorts GROUP BY template HAVING COUNT(*) >= 20 ORDER BY rate DESC, cohort_users DESC, template",
|
|
608
608
|
"config": {
|
|
609
609
|
"xKey": "template",
|
|
610
610
|
"yKey": "rate",
|
|
@@ -639,7 +639,7 @@
|
|
|
639
639
|
"chartType": "bar",
|
|
640
640
|
"source": "first-party",
|
|
641
641
|
"width": 2,
|
|
642
|
-
"sql": "WITH
|
|
642
|
+
"sql": "WITH identity_emails AS (\n SELECT user_key, email\n FROM (\n SELECT NULLIF(properties::jsonb ->> 'auth_user_id', '') AS user_key,\n lower(NULLIF(user_id, '')) AS email,\n ROW_NUMBER() OVER (PARTITION BY NULLIF(properties::jsonb ->> 'auth_user_id', '') ORDER BY timestamp DESC, user_id DESC) AS email_rank\n FROM analytics_events\n WHERE NULLIF(properties::jsonb ->> 'auth_user_id', '') IS NOT NULL\n AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n AND NULLIF(user_id, '') IS NOT NULL\n AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')\n AND event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD')\n ) email_candidates\n WHERE email_rank = 1\n), base AS (SELECT NULLIF(properties::jsonb ->> 'auth_user_id', '') AS user_key, COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown') AS template, event_date AS event_date, user_id FROM analytics_events LEFT JOIN identity_emails ON identity_emails.user_key = NULLIF(properties::jsonb ->> 'auth_user_id', '') WHERE ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND (\n (event_name IN ('action_completed', 'core_action_completed')\n AND COALESCE(properties::jsonb ->> 'success', 'true') = 'true'\n AND NULLIF(properties::jsonb ->> 'output_id', '') IS NOT NULL)\n OR event_name IN ('generation_completed', 'design_created', 'plan_created', 'recording_ready')\n OR event_name = 'run_started'\n OR (event_name = 'app.first_action' AND properties::jsonb ->> 'action' = 'chat_submit')\n OR (event_name = 'core_action_started' AND properties::jsonb ->> 'action_name' = 'chat_submit')\n) AND NULLIF(properties::jsonb ->> 'auth_user_id', '') IS NOT NULL AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND COALESCE(identity_emails.email, '') NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND COALESCE(identity_emails.email, '') LIKE '%@builder.io')) AND COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown') <> 'unknown' AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) <> 'docs' AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides') AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) <> 'www' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')), ranked_first_seen AS (SELECT user_key, template, event_date AS cohort_date, ROW_NUMBER() OVER (PARTITION BY user_key ORDER BY event_date, template) AS rn FROM base), first_seen AS (SELECT user_key, template, cohort_date FROM ranked_first_seen WHERE rn = 1), cohorts AS (SELECT user_key, template, cohort_date FROM first_seen WHERE cohort_date <= to_char(CURRENT_DATE - INTERVAL '14 days', 'YYYY-MM-DD') AND (cohort_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND cohort_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND cohort_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND cohort_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND cohort_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND cohort_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD'))))), cohort_sizes AS (SELECT template, COUNT(DISTINCT user_key) AS users FROM cohorts GROUP BY template), retained AS (SELECT c.template, COUNT(DISTINCT c.user_key) AS retained FROM cohorts c JOIN base b ON b.user_key = c.user_key AND b.event_date >= to_char(c.cohort_date::date + INTERVAL '7 days', 'YYYY-MM-DD') AND b.event_date <= to_char(c.cohort_date::date + INTERVAL '14 days', 'YYYY-MM-DD') GROUP BY c.template) SELECT cs.template, COALESCE(r.retained, 0) AS retained_users, cs.users AS cohort_users, COALESCE(r.retained::float / NULLIF(cs.users, 0), 0) AS rate FROM cohort_sizes cs LEFT JOIN retained r ON r.template = cs.template WHERE cs.users >= 20 ORDER BY rate DESC, cs.users DESC, cs.template",
|
|
643
643
|
"config": {
|
|
644
644
|
"xKey": "template",
|
|
645
645
|
"yKey": "rate",
|
|
@@ -714,7 +714,7 @@
|
|
|
714
714
|
"chartType": "bar",
|
|
715
715
|
"source": "first-party",
|
|
716
716
|
"width": 3,
|
|
717
|
-
"sql": "WITH signup_identity AS (\n SELECT NULLIF(anonymous_id, '') AS anonymous_id,\n MIN(NULLIF(user_id, '')) AS signup_user_id\n FROM analytics_events\n WHERE event_name = 'signup'\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}')))\n AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(user_id, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(user_id, '')) LIKE '%@builder.io'))\n AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}'))\n AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n AND NULLIF(anonymous_id, '') IS NOT NULL\n AND NULLIF(user_id, '') IS NOT NULL\n GROUP BY NULLIF(anonymous_id, '')\n), raw_funnel_events AS (\n SELECT e.*,\n COALESCE(si.signup_user_id, NULLIF(e.user_id, ''), NULLIF(e.anonymous_id, '')) AS funnel_user_key,\n COALESCE(si.signup_user_id, NULLIF(e.user_id, '')) AS funnel_user_email\n FROM analytics_events e\n LEFT JOIN signup_identity si ON si.anonymous_id = NULLIF(e.anonymous_id, '')\n WHERE (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}')))\n AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}'))\n AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n), funnel_events AS (\n SELECT e.*\n FROM raw_funnel_events e\n WHERE (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n), signup_cohort AS (\n SELECT funnel_user_key, MIN(timestamp::timestamptz) AS signup_at\n FROM funnel_events\n WHERE event_name = 'signup'\n AND funnel_user_key IS NOT NULL\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n GROUP BY funnel_user_key\n), cohort_events AS (\n SELECT e.*, c.signup_at\n FROM funnel_events e\n JOIN signup_cohort c ON c.funnel_user_key = e.funnel_user_key\n), funnel_users AS (\n SELECT DISTINCT funnel_user_key\n FROM funnel_events\n WHERE funnel_user_key IS NOT NULL\n), page_stage AS (\n SELECT u.funnel_user_key, c.signup_at, page.page_at\n FROM funnel_users u\n LEFT JOIN signup_cohort c ON c.funnel_user_key = u.funnel_user_key\n LEFT JOIN LATERAL (\n SELECT MIN(e.timestamp::timestamptz) AS page_at\n FROM funnel_events e\n WHERE e.funnel_user_key = u.funnel_user_key\n AND e.event_name = 'auth.signup_viewed'\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n ) page ON true\n), cta_stage AS (\n SELECT p.*, cta.cta_at\n FROM page_stage p\n LEFT JOIN LATERAL (\n SELECT MIN(e.timestamp::timestamptz) AS cta_at\n FROM funnel_events e\n WHERE e.funnel_user_key = p.funnel_user_key\n AND p.page_at IS NOT NULL\n AND e.event_name = 'auth.signup_clicked'\n AND e.timestamp::timestamptz >= p.page_at\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n ) cta ON true\n), signup_stage AS (\n SELECT cta_stage.*,\n CASE WHEN cta_at IS NOT NULL AND signup_at >= cta_at THEN signup_at END AS signed_up_at\n FROM cta_stage\n), onboarding_start_stage AS (\n SELECT s.*, started.started_at\n FROM signup_stage s\n LEFT JOIN LATERAL (\n SELECT MIN(e.timestamp::timestamptz) AS started_at\n FROM cohort_events e\n WHERE e.funnel_user_key = s.funnel_user_key\n AND s.signed_up_at IS NOT NULL\n AND e.event_name = 'onboarding_started'\n AND e.timestamp::timestamptz >= s.signed_up_at\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n ) started ON true\n), step_stage AS (\n SELECT s.*, step.step_at\n FROM onboarding_start_stage s\n LEFT JOIN LATERAL (\n SELECT MIN(e.timestamp::timestamptz) AS step_at\n FROM cohort_events e\n WHERE e.funnel_user_key = s.funnel_user_key\n AND s.started_at IS NOT NULL\n AND e.event_name = 'onboarding_step_viewed'\n AND e.timestamp::timestamptz >= s.started_at\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n ) step ON true\n), completion_stage AS (\n SELECT s.*, completed.completed_at\n FROM step_stage s\n LEFT JOIN LATERAL (\n SELECT MIN(e.timestamp::timestamptz) AS completed_at\n FROM cohort_events e\n WHERE e.funnel_user_key = s.funnel_user_key\n AND s.step_at IS NOT NULL\n AND e.event_name = 'onboarding_completed'\n AND e.timestamp::timestamptz >= s.started_at\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n ) completed ON true\n), entry_stage AS (\n SELECT s.*, entered.entered_at\n FROM completion_stage s\n LEFT JOIN LATERAL (\n SELECT MIN(e.timestamp::timestamptz) AS entered_at\n FROM cohort_events e\n WHERE e.funnel_user_key = s.funnel_user_key\n AND s.completed_at IS NOT NULL\n AND (e.event_name IN ('app_entered', 'onboarding_app_entered') OR (e.event_name = 'session status' AND e.signed_in = 'true'))\n AND e.timestamp::timestamptz >= s.completed_at\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n ) entered ON true\n), action_stage AS (\n SELECT s.*, action.action_at\n FROM entry_stage s\n LEFT JOIN LATERAL (\n SELECT MIN(e.timestamp::timestamptz) AS action_at\n FROM cohort_events e\n WHERE e.funnel_user_key = s.funnel_user_key\n AND s.entered_at IS NOT NULL\n AND ((event_name IN ('action_completed', 'core_action_completed') AND COALESCE(properties::jsonb ->> 'success', 'true') = 'true') OR event_name = 'app.first_action' OR (event_name = 'action.response' AND COALESCE(properties::jsonb ->> 'success', '') = 'true' AND COALESCE(upper(properties::jsonb ->> 'method'), '') <> 'GET'))\n AND e.timestamp::timestamptz >= s.entered_at\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n ) action ON true\n) SELECT 1 AS stage_order, 'Signup page viewed' AS stage, COUNT(*) FILTER (WHERE page_at IS NOT NULL) AS users FROM action_stage UNION ALL SELECT 2, 'Signup CTA clicked', COUNT(*) FILTER (WHERE cta_at IS NOT NULL) FROM action_stage UNION ALL SELECT 3, 'Signed up', COUNT(*) FILTER (WHERE signed_up_at IS NOT NULL) FROM action_stage UNION ALL SELECT 4, 'Onboarding started', COUNT(*) FILTER (WHERE started_at IS NOT NULL) FROM action_stage UNION ALL SELECT 5, 'Onboarding step reached', COUNT(*) FILTER (WHERE step_at IS NOT NULL) FROM action_stage UNION ALL SELECT 6, 'Onboarding completed', COUNT(*) FILTER (WHERE completed_at IS NOT NULL) FROM action_stage UNION ALL SELECT 7, 'Entered app', COUNT(*) FILTER (WHERE entered_at IS NOT NULL) FROM action_stage UNION ALL SELECT 8, 'First significant action', COUNT(*) FILTER (WHERE action_at IS NOT NULL) FROM action_stage ORDER BY stage_order",
|
|
717
|
+
"sql": "WITH auth_identity_bridge AS (\n SELECT NULLIF(anonymous_id, '') AS anonymous_id,\n MIN(NULLIF(properties::jsonb ->> 'auth_user_id', '')) AS auth_user_id\n FROM analytics_events\n WHERE (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}')))\n AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(user_id, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(user_id, '')) LIKE '%@builder.io'))\n AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}'))\n AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n AND NULLIF(anonymous_id, '') IS NOT NULL\n AND NULLIF(properties::jsonb ->> 'auth_user_id', '') IS NOT NULL\n GROUP BY NULLIF(anonymous_id, '')\n HAVING COUNT(DISTINCT NULLIF(properties::jsonb ->> 'auth_user_id', '')) = 1\n), signup_identity AS (\n SELECT NULLIF(e.anonymous_id, '') AS anonymous_id,\n COALESCE(\n MIN(NULLIF(e.properties::jsonb ->> 'auth_user_id', '')),\n MIN(auth_identity_bridge.auth_user_id)\n ) AS signup_auth_user_id,\n MIN(CASE WHEN NULLIF(e.user_id, '') LIKE '%@%.%' THEN e.user_id END) AS signup_user_email\n FROM analytics_events e\n LEFT JOIN auth_identity_bridge\n ON auth_identity_bridge.anonymous_id = NULLIF(e.anonymous_id, '')\n WHERE e.event_name = 'signup'\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}')))\n AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(user_id, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(user_id, '')) LIKE '%@builder.io'))\n AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}'))\n AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n AND NULLIF(e.anonymous_id, '') IS NOT NULL\n AND NULLIF(e.user_id, '') LIKE '%@%.%'\n GROUP BY NULLIF(e.anonymous_id, '')\n HAVING COUNT(DISTINCT NULLIF(e.properties::jsonb ->> 'auth_user_id', '')) <= 1\n), raw_funnel_events AS (\n SELECT e.*,\n COALESCE(\n NULLIF(e.properties::jsonb ->> 'auth_user_id', ''),\n si.signup_auth_user_id,\n si.signup_user_email,\n NULLIF(e.user_id, ''),\n NULLIF(e.anonymous_id, '')\n ) AS funnel_user_key,\n COALESCE(\n CASE WHEN NULLIF(e.user_id, '') LIKE '%@%.%' THEN e.user_id END,\n CASE WHEN NULLIF(e.user_key, '') LIKE '%@%.%' THEN e.user_key END,\n si.signup_user_email\n ) AS funnel_user_email\n FROM analytics_events e\n LEFT JOIN signup_identity si ON si.anonymous_id = NULLIF(e.anonymous_id, '')\n WHERE (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}')))\n AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}'))\n AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n), funnel_events AS (\n SELECT e.*\n FROM raw_funnel_events e\n WHERE (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n), signup_cohort AS (\n SELECT funnel_user_key, MIN(timestamp::timestamptz) AS signup_at\n FROM funnel_events\n WHERE event_name = 'signup'\n AND funnel_user_key IS NOT NULL\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n GROUP BY funnel_user_key\n), cohort_events AS (\n SELECT e.*, c.signup_at\n FROM funnel_events e\n JOIN signup_cohort c ON c.funnel_user_key = e.funnel_user_key\n), funnel_users AS (\n SELECT DISTINCT funnel_user_key\n FROM funnel_events\n WHERE funnel_user_key IS NOT NULL\n), page_stage AS (\n SELECT u.funnel_user_key, c.signup_at, page.page_at\n FROM funnel_users u\n LEFT JOIN signup_cohort c ON c.funnel_user_key = u.funnel_user_key\n LEFT JOIN LATERAL (\n SELECT MIN(e.timestamp::timestamptz) AS page_at\n FROM funnel_events e\n WHERE e.funnel_user_key = u.funnel_user_key\n AND e.event_name = 'auth.signup_viewed'\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n ) page ON true\n), cta_stage AS (\n SELECT p.*, cta.cta_at\n FROM page_stage p\n LEFT JOIN LATERAL (\n SELECT MIN(e.timestamp::timestamptz) AS cta_at\n FROM funnel_events e\n WHERE e.funnel_user_key = p.funnel_user_key\n AND p.page_at IS NOT NULL\n AND e.event_name = 'auth.signup_clicked'\n AND e.timestamp::timestamptz >= p.page_at\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n ) cta ON true\n), signup_stage AS (\n SELECT cta_stage.*,\n CASE WHEN cta_at IS NOT NULL AND signup_at >= cta_at THEN signup_at END AS signed_up_at\n FROM cta_stage\n), onboarding_start_stage AS (\n SELECT s.*, started.started_at\n FROM signup_stage s\n LEFT JOIN LATERAL (\n SELECT MIN(e.timestamp::timestamptz) AS started_at\n FROM cohort_events e\n WHERE e.funnel_user_key = s.funnel_user_key\n AND s.signed_up_at IS NOT NULL\n AND e.event_name = 'onboarding_started'\n AND e.timestamp::timestamptz >= s.signed_up_at\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n ) started ON true\n), step_stage AS (\n SELECT s.*, step.step_at\n FROM onboarding_start_stage s\n LEFT JOIN LATERAL (\n SELECT MIN(e.timestamp::timestamptz) AS step_at\n FROM cohort_events e\n WHERE e.funnel_user_key = s.funnel_user_key\n AND s.started_at IS NOT NULL\n AND e.event_name = 'onboarding_step_viewed'\n AND e.timestamp::timestamptz >= s.started_at\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n ) step ON true\n), completion_stage AS (\n SELECT s.*, completed.completed_at\n FROM step_stage s\n LEFT JOIN LATERAL (\n SELECT MIN(e.timestamp::timestamptz) AS completed_at\n FROM cohort_events e\n WHERE e.funnel_user_key = s.funnel_user_key\n AND s.step_at IS NOT NULL\n AND e.event_name = 'onboarding_completed'\n AND e.timestamp::timestamptz >= s.started_at\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n ) completed ON true\n), entry_stage AS (\n SELECT s.*, entered.entered_at\n FROM completion_stage s\n LEFT JOIN LATERAL (\n SELECT MIN(e.timestamp::timestamptz) AS entered_at\n FROM cohort_events e\n WHERE e.funnel_user_key = s.funnel_user_key\n AND s.completed_at IS NOT NULL\n AND (e.event_name IN ('app_entered', 'onboarding_app_entered') OR (e.event_name = 'session status' AND e.signed_in = 'true'))\n AND e.timestamp::timestamptz >= s.completed_at\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n ) entered ON true\n), action_stage AS (\n SELECT s.*, action.action_at\n FROM entry_stage s\n LEFT JOIN LATERAL (\n SELECT MIN(e.timestamp::timestamptz) AS action_at\n FROM cohort_events e\n WHERE e.funnel_user_key = s.funnel_user_key\n AND s.entered_at IS NOT NULL\n AND ((event_name IN ('action_completed', 'core_action_completed') AND COALESCE(properties::jsonb ->> 'success', 'true') = 'true') OR event_name = 'app.first_action' OR (event_name = 'action.response' AND COALESCE(properties::jsonb ->> 'success', '') = 'true' AND COALESCE(upper(properties::jsonb ->> 'method'), '') <> 'GET'))\n AND e.timestamp::timestamptz >= s.entered_at\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = 'custom' AND event_date >= '{{timeRangeStart}}' AND event_date <= '{{timeRangeEnd}}'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n ) action ON true\n) SELECT 1 AS stage_order, 'Signup page viewed' AS stage, COUNT(*) FILTER (WHERE page_at IS NOT NULL) AS users FROM action_stage UNION ALL SELECT 2, 'Signup CTA clicked', COUNT(*) FILTER (WHERE cta_at IS NOT NULL) FROM action_stage UNION ALL SELECT 3, 'Signed up', COUNT(*) FILTER (WHERE signed_up_at IS NOT NULL) FROM action_stage UNION ALL SELECT 4, 'Onboarding started', COUNT(*) FILTER (WHERE started_at IS NOT NULL) FROM action_stage UNION ALL SELECT 5, 'Onboarding step reached', COUNT(*) FILTER (WHERE step_at IS NOT NULL) FROM action_stage UNION ALL SELECT 6, 'Onboarding completed', COUNT(*) FILTER (WHERE completed_at IS NOT NULL) FROM action_stage UNION ALL SELECT 7, 'Entered app', COUNT(*) FILTER (WHERE entered_at IS NOT NULL) FROM action_stage UNION ALL SELECT 8, 'First significant action', COUNT(*) FILTER (WHERE action_at IS NOT NULL) FROM action_stage ORDER BY stage_order",
|
|
718
718
|
"config": {
|
|
719
719
|
"xKey": "stage",
|
|
720
720
|
"yKey": "users",
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
|
|
1
3
|
import { loadDashboardSeed } from "./dashboard-seeds";
|
|
2
4
|
import {
|
|
3
5
|
buildPanel,
|
|
@@ -22,6 +24,14 @@ export const FIRST_PARTY_BIGQUERY_DASHBOARD_ID =
|
|
|
22
24
|
const BIGQUERY_SESSION_STATUS_EVENT_FILTER =
|
|
23
25
|
"event_name IN ('session status', 'session_status')";
|
|
24
26
|
const BIGQUERY_SIGNED_IN_ACTIVITY_FILTER = `(((${BIGQUERY_SESSION_STATUS_EVENT_FILTER} AND signed_in = 'true') OR (event_name = 'app_entered' AND NULLIF(user_id, '') IS NOT NULL)) AND NULLIF(user_key, '') IS NOT NULL)`;
|
|
27
|
+
const BIGQUERY_CONTENT_OR_CHAT_ACTIVITY_FILTER = `(
|
|
28
|
+
(event_name IN ('action_completed', 'core_action_completed')
|
|
29
|
+
AND COALESCE(JSON_VALUE(properties, '$.success'), 'true') = 'true'
|
|
30
|
+
AND NULLIF(JSON_VALUE(properties, '$.output_id'), '') IS NOT NULL)
|
|
31
|
+
OR event_name IN ('generation_completed', 'design_created', 'plan_created', 'recording_ready', 'run_started')
|
|
32
|
+
OR (event_name = 'app.first_action' AND JSON_VALUE(properties, '$.action') = 'chat_submit')
|
|
33
|
+
OR (event_name = 'core_action_started' AND JSON_VALUE(properties, '$.action_name') = 'chat_submit')
|
|
34
|
+
) AND NULLIF(JSON_VALUE(properties, '$.auth_user_id'), '') IS NOT NULL`;
|
|
25
35
|
|
|
26
36
|
export const FIRST_PARTY_BIGQUERY_WAU_SQL = `WITH base AS (
|
|
27
37
|
SELECT
|
|
@@ -165,18 +175,60 @@ const PRE_CUSTOM_RETENTION_ANCHOR_RANGE =
|
|
|
165
175
|
"UNNEST(GENERATE_DATE_ARRAY(DATE_SUB(CURRENT_DATE(), INTERVAL n - 1 DAY), CURRENT_DATE())) AS date";
|
|
166
176
|
const CUSTOM_RETENTION_ANCHOR_RANGE =
|
|
167
177
|
"UNNEST(GENERATE_DATE_ARRAY(\n CASE WHEN '{{timeRange}}' = 'custom' THEN DATE('{{timeRangeStart}}') ELSE DATE_SUB(CURRENT_DATE(), INTERVAL n - 1 DAY) END,\n CASE WHEN '{{timeRange}}' = 'custom' THEN LEAST(DATE('{{timeRangeEnd}}'), CURRENT_DATE()) ELSE CURRENT_DATE() END\n )) AS date";
|
|
178
|
+
const LEGACY_BIGQUERY_RETENTION_TEMPLATE_FILTER =
|
|
179
|
+
"LOWER(COALESCE(NULLIF(template, ''), NULLIF(JSON_VALUE(properties, '$.templateId'), ''), NULLIF(app, ''), NULLIF(JSON_VALUE(properties, '$.agent_native_app'), ''), 'unknown'))";
|
|
180
|
+
const BIGQUERY_RETENTION_TEMPLATE_FILTER =
|
|
181
|
+
"LOWER(COALESCE(NULLIF(template, ''), NULLIF(JSON_VALUE(properties, '$.templateId'), ''), NULLIF(JSON_VALUE(properties, '$.agent_native_template'), ''), NULLIF(JSON_VALUE(properties, '$.agentNativeTemplate'), ''), NULLIF(app, ''), NULLIF(JSON_VALUE(properties, '$.agent_native_app'), ''), NULLIF(JSON_VALUE(properties, '$.agentNativeApp'), ''), 'unknown'))";
|
|
182
|
+
const BIGQUERY_RETENTION_IDENTITY_EMAILS_CTE = `identity_emails AS (
|
|
183
|
+
SELECT
|
|
184
|
+
NULLIF(JSON_VALUE(properties, '$.auth_user_id'), '') AS user_key,
|
|
185
|
+
LOWER(ARRAY_AGG(NULLIF(user_id, '') IGNORE NULLS ORDER BY timestamp DESC, user_id DESC LIMIT 1)[SAFE_OFFSET(0)]) AS email
|
|
186
|
+
FROM \`builder-3b0a2.analytics.first_party_analytics_events_raw\`
|
|
187
|
+
WHERE org_id = 'PlRt3bfcpJNnOyF_Wfgsh'
|
|
188
|
+
AND NULLIF(JSON_VALUE(properties, '$.auth_user_id'), '') IS NOT NULL
|
|
189
|
+
AND ${BIGQUERY_RETENTION_TEMPLATE_FILTER} IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')
|
|
190
|
+
AND event_date >= IF('{{timeRange}}' = 'custom', DATE_SUB(DATE('{{timeRangeStart}}'), INTERVAL 371 DAY), DATE_SUB(CURRENT_DATE(), INTERVAL 371 DAY))
|
|
191
|
+
AND event_date <= CURRENT_DATE()
|
|
192
|
+
GROUP BY 1
|
|
193
|
+
)`;
|
|
194
|
+
const LEGACY_BIGQUERY_RETENTION_EMAIL_FILTER = ` AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND LOWER(COALESCE(NULLIF(user_id, ''), '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND LOWER(COALESCE(NULLIF(user_id, ''), '')) LIKE '%@builder.io'))`;
|
|
195
|
+
const BIGQUERY_RETENTION_IDENTITY_EMAIL_FILTER = ` AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND COALESCE(identity_emails.email, '') NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND COALESCE(identity_emails.email, '') LIKE '%@builder.io'))`;
|
|
168
196
|
|
|
169
197
|
export const FIRST_PARTY_BIGQUERY_RETENTION_SQL =
|
|
170
198
|
LEGACY_FIRST_PARTY_BIGQUERY_RETENTION_SQL.replace(
|
|
171
|
-
"
|
|
172
|
-
|
|
199
|
+
"WITH base AS (",
|
|
200
|
+
`WITH ${BIGQUERY_RETENTION_IDENTITY_EMAILS_CTE},\nbase AS (`,
|
|
173
201
|
)
|
|
202
|
+
.replace(
|
|
203
|
+
LEGACY_BIGQUERY_RETENTION_TEMPLATE_FILTER,
|
|
204
|
+
BIGQUERY_RETENTION_TEMPLATE_FILTER,
|
|
205
|
+
)
|
|
206
|
+
.replace(
|
|
207
|
+
"FROM `builder-3b0a2.analytics.first_party_analytics_events_raw`\nWHERE event_name = 'session status'",
|
|
208
|
+
"FROM `builder-3b0a2.analytics.first_party_analytics_events_raw` AS events\nLEFT JOIN identity_emails ON identity_emails.user_key = NULLIF(JSON_VALUE(events.properties, '$.auth_user_id'), '')\nWHERE event_name = 'session status'",
|
|
209
|
+
)
|
|
210
|
+
.replace(
|
|
211
|
+
LEGACY_BIGQUERY_RETENTION_EMAIL_FILTER,
|
|
212
|
+
BIGQUERY_RETENTION_IDENTITY_EMAIL_FILTER,
|
|
213
|
+
)
|
|
214
|
+
.replace(
|
|
215
|
+
"event_name = 'session status'\n AND signed_in = 'true'\n AND NULLIF(user_key, '') IS NOT NULL",
|
|
216
|
+
BIGQUERY_SIGNED_IN_ACTIVITY_FILTER,
|
|
217
|
+
)
|
|
174
218
|
.split("event_name = 'session status'")
|
|
175
219
|
.join(BIGQUERY_SESSION_STATUS_EVENT_FILTER)
|
|
220
|
+
.replace(
|
|
221
|
+
BIGQUERY_SIGNED_IN_ACTIVITY_FILTER,
|
|
222
|
+
BIGQUERY_CONTENT_OR_CHAT_ACTIVITY_FILTER,
|
|
223
|
+
)
|
|
224
|
+
.replace(
|
|
225
|
+
"SELECT NULLIF(user_key, '') AS user_key",
|
|
226
|
+
"SELECT NULLIF(JSON_VALUE(properties, '$.auth_user_id'), '') AS user_key",
|
|
227
|
+
)
|
|
176
228
|
.replace(PRE_CUSTOM_RETENTION_BASE_RANGE, CUSTOM_RETENTION_BASE_RANGE)
|
|
177
229
|
.replace(
|
|
178
230
|
"all_r AS (SELECT * FROM r1 UNION ALL SELECT * FROM r2),\nperiods AS",
|
|
179
|
-
`all_r AS (SELECT * FROM r1 UNION ALL SELECT * FROM r2),\ncoverage_dates AS (\n SELECT DISTINCT event_date\n FROM \`builder-3b0a2.analytics.first_party_analytics_events_raw\`\n WHERE org_id = 'PlRt3bfcpJNnOyF_Wfgsh'\n AND ${
|
|
231
|
+
`all_r AS (SELECT * FROM r1 UNION ALL SELECT * FROM r2),\ncoverage_dates AS (\n SELECT DISTINCT event_date\n FROM \`builder-3b0a2.analytics.first_party_analytics_events_raw\`\n WHERE org_id = 'PlRt3bfcpJNnOyF_Wfgsh'\n AND ${BIGQUERY_CONTENT_OR_CHAT_ACTIVITY_FILTER}\n AND event_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 365 DAY)\n AND event_date <= CURRENT_DATE()\n),\nperiods AS`,
|
|
180
232
|
)
|
|
181
233
|
.replace(
|
|
182
234
|
PRE_CUSTOM_RETENTION_COVERAGE_RANGE,
|
|
@@ -200,6 +252,31 @@ export const FIRST_PARTY_BIGQUERY_RETENTION_SQL =
|
|
|
200
252
|
)
|
|
201
253
|
.replace(PRE_CUSTOM_RETENTION_ANCHOR_RANGE, CUSTOM_RETENTION_ANCHOR_RANGE);
|
|
202
254
|
|
|
255
|
+
export const PREVIOUS_CANONICAL_FIRST_PARTY_BIGQUERY_RETENTION_SQL =
|
|
256
|
+
FIRST_PARTY_BIGQUERY_RETENTION_SQL.split(
|
|
257
|
+
"NULLIF(JSON_VALUE(properties, '$.agent_native_template'), ''), ",
|
|
258
|
+
)
|
|
259
|
+
.join("")
|
|
260
|
+
.split("NULLIF(JSON_VALUE(properties, '$.agentNativeTemplate'), ''), ")
|
|
261
|
+
.join("")
|
|
262
|
+
.split("NULLIF(JSON_VALUE(properties, '$.agentNativeApp'), ''), ")
|
|
263
|
+
.join("")
|
|
264
|
+
.replace(
|
|
265
|
+
` AND ${BIGQUERY_CONTENT_OR_CHAT_ACTIVITY_FILTER}\n${CUSTOM_RETENTION_COVERAGE_RANGE}`,
|
|
266
|
+
CUSTOM_RETENTION_COVERAGE_RANGE,
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
export const PREVIOUS_PRE_CUSTOM_FIRST_PARTY_BIGQUERY_RETENTION_SQL =
|
|
270
|
+
PREVIOUS_CANONICAL_FIRST_PARTY_BIGQUERY_RETENTION_SQL.replace(
|
|
271
|
+
CUSTOM_RETENTION_BASE_RANGE,
|
|
272
|
+
PRE_CUSTOM_RETENTION_BASE_RANGE,
|
|
273
|
+
)
|
|
274
|
+
.replace(
|
|
275
|
+
CUSTOM_RETENTION_COVERAGE_RANGE,
|
|
276
|
+
PRE_CUSTOM_RETENTION_COVERAGE_RANGE,
|
|
277
|
+
)
|
|
278
|
+
.replace(CUSTOM_RETENTION_ANCHOR_RANGE, PRE_CUSTOM_RETENTION_ANCHOR_RANGE);
|
|
279
|
+
|
|
203
280
|
const PRE_CUSTOM_FIRST_PARTY_BIGQUERY_RETENTION_SQL =
|
|
204
281
|
FIRST_PARTY_BIGQUERY_RETENTION_SQL.replace(
|
|
205
282
|
CUSTOM_RETENTION_BASE_RANGE,
|
|
@@ -294,6 +371,8 @@ function repairFirstPartyBigQueryDauSql(sql: string): string {
|
|
|
294
371
|
function isLegacyFirstPartyBigQueryRetentionSql(sql: string): boolean {
|
|
295
372
|
return [
|
|
296
373
|
LEGACY_FIRST_PARTY_BIGQUERY_RETENTION_SQL,
|
|
374
|
+
PREVIOUS_CANONICAL_FIRST_PARTY_BIGQUERY_RETENTION_SQL,
|
|
375
|
+
PREVIOUS_PRE_CUSTOM_FIRST_PARTY_BIGQUERY_RETENTION_SQL,
|
|
297
376
|
PRE_CUSTOM_FIRST_PARTY_BIGQUERY_RETENTION_SQL,
|
|
298
377
|
PRE_CUSTOM_FIRST_PARTY_BIGQUERY_RETENTION_WITH_LAST_VALID_SQL,
|
|
299
378
|
].some(
|
|
@@ -370,6 +449,102 @@ const NEW_VS_RECURRING_USERS_SQL = `WITH activity AS (SELECT NULLIF(user_key, ''
|
|
|
370
449
|
const NEW_VS_RECURRING_USERS_DESCRIPTION =
|
|
371
450
|
"Daily signed-in visitors split by first active day observed in the previous 365 days (New) vs return visit (Recurring), stacked with Recurring on the bottom and New on top. Docs and marketing-site traffic are excluded.";
|
|
372
451
|
|
|
452
|
+
type FingerprintedPanelReplacement = {
|
|
453
|
+
id: string;
|
|
454
|
+
source: "first-party" | "bigquery";
|
|
455
|
+
sha256: string;
|
|
456
|
+
sql: string;
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
// Exact normalized query fingerprints from origin/main@452757b243ef, retained
|
|
460
|
+
// after refreshing the catalog and shipped seed removed these signatures.
|
|
461
|
+
const ORIGIN_MAIN_PANEL_REPLACEMENTS: readonly FingerprintedPanelReplacement[] =
|
|
462
|
+
[
|
|
463
|
+
{
|
|
464
|
+
id: "retention-over-time",
|
|
465
|
+
source: "first-party",
|
|
466
|
+
sha256:
|
|
467
|
+
"974b66473d3a9590bdaa9ccb6df9da2fc149008e07ce9838e56dcc8a478d6f6f",
|
|
468
|
+
sql: buildPanel("retention-over-time")!.sql,
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
id: "retention-over-time",
|
|
472
|
+
source: "first-party",
|
|
473
|
+
sha256:
|
|
474
|
+
"b395a694889d393d889ced4a79d936e17cb58af88b856670a393cba91e3cf83d",
|
|
475
|
+
sql: buildPanel("retention-over-time")!.sql,
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
id: "one-day-retention-by-template",
|
|
479
|
+
source: "first-party",
|
|
480
|
+
sha256:
|
|
481
|
+
"84049de619edfca2a8ce10f2da0f6acf674aa504eca65c242a4e76ffd0b22bf1",
|
|
482
|
+
sql: buildPanel("one-day-retention-by-template")!.sql,
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
id: "seven-day-retention-by-template",
|
|
486
|
+
source: "first-party",
|
|
487
|
+
sha256:
|
|
488
|
+
"3e8993b8d56fd33a3883bb57f832bbb539ee9750a563fe1b21eb10e12aec7966",
|
|
489
|
+
sql: buildPanel("seven-day-retention-by-template")!.sql,
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
id: "activation-funnel",
|
|
493
|
+
source: "first-party",
|
|
494
|
+
sha256:
|
|
495
|
+
"ba2e861c2044e2fec669bcb15a878a675eaf426f51bcfe4db32ebd228d2050f0",
|
|
496
|
+
sql: buildPanel("activation-funnel")!.sql,
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
id: "retention-over-time",
|
|
500
|
+
source: "bigquery",
|
|
501
|
+
sha256:
|
|
502
|
+
"d7b7a0400b5f1228dade65d89a9b576d5d98e387b8818b0177c6744805ea9b23",
|
|
503
|
+
sql: FIRST_PARTY_BIGQUERY_RETENTION_SQL,
|
|
504
|
+
},
|
|
505
|
+
];
|
|
506
|
+
|
|
507
|
+
function fingerprintPanelSql(sql: string): string {
|
|
508
|
+
return createHash("sha256")
|
|
509
|
+
.update(sql.replace(/\s+/g, " ").trim())
|
|
510
|
+
.digest("hex");
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function repairFingerprintedPanelQueries(
|
|
514
|
+
config: Record<string, unknown>,
|
|
515
|
+
replacements: readonly FingerprintedPanelReplacement[],
|
|
516
|
+
): { config: Record<string, unknown>; changed: boolean } {
|
|
517
|
+
if (!Array.isArray(config.panels)) return { config, changed: false };
|
|
518
|
+
|
|
519
|
+
let changed = false;
|
|
520
|
+
const panels = config.panels.map((rawPanel) => {
|
|
521
|
+
if (!rawPanel || typeof rawPanel !== "object") return rawPanel;
|
|
522
|
+
const panel = rawPanel as Record<string, unknown>;
|
|
523
|
+
if (
|
|
524
|
+
typeof panel.id !== "string" ||
|
|
525
|
+
(panel.source !== "first-party" && panel.source !== "bigquery") ||
|
|
526
|
+
typeof panel.sql !== "string"
|
|
527
|
+
) {
|
|
528
|
+
return rawPanel;
|
|
529
|
+
}
|
|
530
|
+
const fingerprint = fingerprintPanelSql(panel.sql);
|
|
531
|
+
const replacement = replacements.find(
|
|
532
|
+
(candidate) =>
|
|
533
|
+
candidate.id === panel.id &&
|
|
534
|
+
candidate.source === panel.source &&
|
|
535
|
+
candidate.sha256 === fingerprint,
|
|
536
|
+
);
|
|
537
|
+
if (!replacement) return rawPanel;
|
|
538
|
+
|
|
539
|
+
changed = true;
|
|
540
|
+
return { ...panel, sql: replacement.sql };
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
return changed
|
|
544
|
+
? { config: { ...config, panels }, changed }
|
|
545
|
+
: { config, changed };
|
|
546
|
+
}
|
|
547
|
+
|
|
373
548
|
const CANONICAL_CUSTOM_PANEL_REPLACEMENTS: readonly ExactFirstPartyPanelReplacement[] =
|
|
374
549
|
[
|
|
375
550
|
{
|
|
@@ -447,10 +622,19 @@ const CANONICAL_CATALOG_PANEL_REPLACEMENTS: readonly ExactFirstPartyPanelReplace
|
|
|
447
622
|
export function repairCanonicalFirstPartyDashboardQueries(
|
|
448
623
|
config: Record<string, unknown>,
|
|
449
624
|
) {
|
|
450
|
-
|
|
625
|
+
const historical = repairFingerprintedPanelQueries(
|
|
626
|
+
config,
|
|
627
|
+
ORIGIN_MAIN_PANEL_REPLACEMENTS.filter(
|
|
628
|
+
(replacement) => replacement.source === "first-party",
|
|
629
|
+
),
|
|
630
|
+
);
|
|
631
|
+
const repaired = repairFirstPartyObservedRetentionPanels(historical.config, [
|
|
451
632
|
...CANONICAL_CUSTOM_PANEL_REPLACEMENTS,
|
|
452
633
|
...CANONICAL_CATALOG_PANEL_REPLACEMENTS,
|
|
453
634
|
]);
|
|
635
|
+
return historical.changed && !repaired.changed
|
|
636
|
+
? { ...repaired, changed: true }
|
|
637
|
+
: repaired;
|
|
454
638
|
}
|
|
455
639
|
|
|
456
640
|
export function repairKnownFirstPartyDashboardQueries(
|
|
@@ -458,7 +642,18 @@ export function repairKnownFirstPartyDashboardQueries(
|
|
|
458
642
|
config: Record<string, unknown>,
|
|
459
643
|
): { config: Record<string, unknown>; changed: boolean } {
|
|
460
644
|
if (dashboardId === FIRST_PARTY_BIGQUERY_DASHBOARD_ID) {
|
|
461
|
-
|
|
645
|
+
const historical = repairFingerprintedPanelQueries(
|
|
646
|
+
config,
|
|
647
|
+
ORIGIN_MAIN_PANEL_REPLACEMENTS.filter(
|
|
648
|
+
(replacement) => replacement.source === "bigquery",
|
|
649
|
+
),
|
|
650
|
+
);
|
|
651
|
+
const repaired = repairFirstPartyBigQueryDashboardQueries(
|
|
652
|
+
historical.config,
|
|
653
|
+
);
|
|
654
|
+
return historical.changed && !repaired.changed
|
|
655
|
+
? { ...repaired, changed: true }
|
|
656
|
+
: repaired;
|
|
462
657
|
}
|
|
463
658
|
if (dashboardId === FIRST_PARTY_DASHBOARD_ID) {
|
|
464
659
|
return repairCanonicalFirstPartyDashboardQueries(config);
|