From 6a9219457a493627b4047ff7e95c5fb8aed7a9eb Mon Sep 17 00:00:00 2001 From: "wizard-ci[bot]" Date: Wed, 14 Jan 2026 16:15:33 +0000 Subject: [PATCH] wizard-ci: next-js/15-pages-router-todo --- .../components/todos/todo-list.tsx | 35 ++ .../instrumentation-client.ts | 14 + .../15-pages-router-todo/next.config.ts | 15 + .../next-js/15-pages-router-todo/package.json | 1 + .../15-pages-router-todo/pnpm-lock.yaml | 346 +++++++++++++++++- .../posthog-setup-report.md | 71 ++++ 6 files changed, 480 insertions(+), 2 deletions(-) create mode 100644 apps/next-js/15-pages-router-todo/instrumentation-client.ts create mode 100644 apps/next-js/15-pages-router-todo/posthog-setup-report.md diff --git a/apps/next-js/15-pages-router-todo/components/todos/todo-list.tsx b/apps/next-js/15-pages-router-todo/components/todos/todo-list.tsx index 9bad4230..d0bdf857 100644 --- a/apps/next-js/15-pages-router-todo/components/todos/todo-list.tsx +++ b/apps/next-js/15-pages-router-todo/components/todos/todo-list.tsx @@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'; import Link from 'next/link'; +import posthog from 'posthog-js'; import { Todo } from '@/lib/data'; import { TodoForm } from './todo-form'; import { TodoItem } from './todo-item'; @@ -24,6 +25,10 @@ export function TodoList() { } } catch (error) { console.error('Failed to fetch todos:', error); + posthog.capture('todos_fetch_failed', { + error: error instanceof Error ? error.message : 'Unknown error', + }); + posthog.captureException(error); } finally { setLoading(false); } @@ -42,9 +47,17 @@ export function TodoList() { if (response.ok) { const newTodo = await response.json(); setTodos([...todos, newTodo]); + posthog.capture('todo_created', { + todo_id: newTodo.id, + has_description: !!description, + }); } } catch (error) { console.error('Failed to add todo:', error); + posthog.capture('todo_create_failed', { + error: error instanceof Error ? error.message : 'Unknown error', + }); + posthog.captureException(error); } }; @@ -61,9 +74,23 @@ export function TodoList() { if (response.ok) { const updatedTodo = await response.json(); setTodos(todos.map((todo) => (todo.id === id ? updatedTodo : todo))); + if (completed) { + posthog.capture('todo_completed', { + todo_id: id, + }); + } else { + posthog.capture('todo_uncompleted', { + todo_id: id, + }); + } } } catch (error) { console.error('Failed to update todo:', error); + posthog.capture('todo_update_failed', { + todo_id: id, + error: error instanceof Error ? error.message : 'Unknown error', + }); + posthog.captureException(error); } }; @@ -75,9 +102,17 @@ export function TodoList() { if (response.ok) { setTodos(todos.filter((todo) => todo.id !== id)); + posthog.capture('todo_deleted', { + todo_id: id, + }); } } catch (error) { console.error('Failed to delete todo:', error); + posthog.capture('todo_delete_failed', { + todo_id: id, + error: error instanceof Error ? error.message : 'Unknown error', + }); + posthog.captureException(error); } }; diff --git a/apps/next-js/15-pages-router-todo/instrumentation-client.ts b/apps/next-js/15-pages-router-todo/instrumentation-client.ts new file mode 100644 index 00000000..1c2129d5 --- /dev/null +++ b/apps/next-js/15-pages-router-todo/instrumentation-client.ts @@ -0,0 +1,14 @@ +import posthog from "posthog-js" + +posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { + api_host: "/ingest", + ui_host: "https://us.posthog.com", + // Include the defaults option as required by PostHog + defaults: '2025-05-24', + // Enables capturing unhandled exceptions via Error Tracking + capture_exceptions: true, + // Turn on debug in development mode + debug: process.env.NODE_ENV === "development", +}); + +//IMPORTANT: Never combine this approach with other client-side PostHog initialization approaches, especially components like a PostHogProvider. instrumentation-client.ts is the correct solution for initializating client-side PostHog in Next.js 15.3+ apps. diff --git a/apps/next-js/15-pages-router-todo/next.config.ts b/apps/next-js/15-pages-router-todo/next.config.ts index fdcdf637..d55475bb 100644 --- a/apps/next-js/15-pages-router-todo/next.config.ts +++ b/apps/next-js/15-pages-router-todo/next.config.ts @@ -2,6 +2,21 @@ import type { NextConfig } from 'next'; const nextConfig: NextConfig = { // Configuration for stable Next.js 15 + reactStrictMode: true, + async rewrites() { + return [ + { + source: "/ingest/static/:path*", + destination: "https://us-assets.i.posthog.com/static/:path*", + }, + { + source: "/ingest/:path*", + destination: "https://us.i.posthog.com/:path*", + }, + ]; + }, + // This is required to support PostHog trailing slash API requests + skipTrailingSlashRedirect: true, }; export default nextConfig; diff --git a/apps/next-js/15-pages-router-todo/package.json b/apps/next-js/15-pages-router-todo/package.json index 86139844..3af2d7ce 100644 --- a/apps/next-js/15-pages-router-todo/package.json +++ b/apps/next-js/15-pages-router-todo/package.json @@ -16,6 +16,7 @@ "lucide-react": "^0.511.0", "next": "15.5.7", "postcss": "^8.5.3", + "posthog-js": "^1.321.1", "radix-ui": "^1.4.2", "react": "19.1.2", "react-dom": "19.1.2", diff --git a/apps/next-js/15-pages-router-todo/pnpm-lock.yaml b/apps/next-js/15-pages-router-todo/pnpm-lock.yaml index dd61a5aa..085d0cad 100644 --- a/apps/next-js/15-pages-router-todo/pnpm-lock.yaml +++ b/apps/next-js/15-pages-router-todo/pnpm-lock.yaml @@ -34,10 +34,13 @@ importers: version: 0.511.0(react@19.1.2) next: specifier: 15.5.7 - version: 15.5.7(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + version: 15.5.7(@opentelemetry/api@1.9.0)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) postcss: specifier: ^8.5.3 version: 8.5.6 + posthog-js: + specifier: ^1.321.1 + version: 1.321.1 radix-ui: specifier: ^1.4.2 version: 1.4.3(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) @@ -296,6 +299,114 @@ packages: cpu: [x64] os: [win32] + '@opentelemetry/api-logs@0.208.0': + resolution: {integrity: sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/core@2.2.0': + resolution: {integrity: sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@2.3.0': + resolution: {integrity: sha512-PcmxJQzs31cfD0R2dE91YGFcLxOSN4Bxz7gez5UwSUjCai8BwH/GI5HchfVshHkWdTkUs0qcaPJgVHKXUp7I3A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/exporter-logs-otlp-http@0.208.0': + resolution: {integrity: sha512-jOv40Bs9jy9bZVLo/i8FwUiuCvbjWDI+ZW13wimJm4LjnlwJxGgB+N/VWOZUTpM+ah/awXeQqKdNlpLf2EjvYg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-exporter-base@0.208.0': + resolution: {integrity: sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-transformer@0.208.0': + resolution: {integrity: sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/resources@2.2.0': + resolution: {integrity: sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/resources@2.3.0': + resolution: {integrity: sha512-shlr2l5g+87J8wqYlsLyaUsgKVRO7RtX70Ckd5CtDOWtImZgaUDmf4Z2ozuSKQLM2wPDR0TE/3bPVBNJtRm/cQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-logs@0.208.0': + resolution: {integrity: sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.10.0' + + '@opentelemetry/sdk-metrics@2.2.0': + resolution: {integrity: sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.9.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@2.2.0': + resolution: {integrity: sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/semantic-conventions@1.38.0': + resolution: {integrity: sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==} + engines: {node: '>=14'} + + '@posthog/core@1.9.1': + resolution: {integrity: sha512-kRb1ch2dhQjsAapZmu6V66551IF2LnCbc1rnrQqnR7ArooVyJN9KOPXre16AJ3ObJz2eTfuP7x25BMyS2Y5Exw==} + + '@posthog/types@1.321.1': + resolution: {integrity: sha512-GPVGSvP/uLk/VLQBOnLdRhCoLmce2r0YQ4j4X2aC0fRvPYepJC+O8S0ESHg5vZW4abGrxQktaCzx7jFZur5lBw==} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + '@radix-ui/number@1.1.1': resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} @@ -1088,6 +1199,9 @@ packages: '@types/react@19.1.4': resolution: {integrity: sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g==} + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + aria-hidden@1.2.6: resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} engines: {node: '>=10'} @@ -1125,6 +1239,13 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + core-js@3.47.0: + resolution: {integrity: sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -1135,6 +1256,9 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + dompurify@3.3.1: + resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==} + electron-to-chromium@1.5.263: resolution: {integrity: sha512-DrqJ11Knd+lo+dv+lltvfMDLU27g14LMdH2b0O3Pio4uk0x+z7OR+JrmyacTPN2M8w3BrZ7/RTwG3R9B7irPlg==} @@ -1146,6 +1270,9 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + fflate@0.4.8: + resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==} + fraction.js@5.3.4: resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} @@ -1156,6 +1283,9 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true @@ -1224,6 +1354,9 @@ packages: resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} engines: {node: '>= 12.0.0'} + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + lucide-react@0.511.0: resolution: {integrity: sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w==} peerDependencies: @@ -1273,6 +1406,10 @@ packages: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1287,6 +1424,19 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + posthog-js@1.321.1: + resolution: {integrity: sha512-PsRkY3LvPg3VUKROvc3HwSGUU9/JKHx/cNS4rZ5uHD5tDRACjjguGgFTp9XBLuf/+9YZcfdg1wxidk5bODvc2w==} + + preact@10.28.2: + resolution: {integrity: sha512-lbteaWGzGHdlIuiJ0l2Jq454m6kcpI1zNje6d8MlGAFlYvP2GO4ibnat7P74Esfz4sPTdM6UxtTwh/d3pwM9JA==} + + protobufjs@7.5.4: + resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} + engines: {node: '>=12.0.0'} + + query-selector-shadow-dom@1.0.1: + resolution: {integrity: sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==} + radix-ui@1.4.3: resolution: {integrity: sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA==} peerDependencies: @@ -1351,6 +1501,14 @@ packages: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -1427,6 +1585,14 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + web-vitals@4.2.4: + resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + yallist@5.0.0: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} @@ -1606,6 +1772,111 @@ snapshots: '@next/swc-win32-x64-msvc@15.5.7': optional: true + '@opentelemetry/api-logs@0.208.0': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/api@1.9.0': {} + + '@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.38.0 + + '@opentelemetry/core@2.3.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.38.0 + + '@opentelemetry/exporter-logs-otlp-http@0.208.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.208.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/otlp-exporter-base@0.208.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/otlp-transformer@0.208.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.208.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) + protobufjs: 7.5.4 + + '@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.38.0 + + '@opentelemetry/resources@2.3.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.3.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.38.0 + + '@opentelemetry/sdk-logs@0.208.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.208.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/sdk-metrics@2.2.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.38.0 + + '@opentelemetry/semantic-conventions@1.38.0': {} + + '@posthog/core@1.9.1': + dependencies: + cross-spawn: 7.0.6 + + '@posthog/types@1.321.1': {} + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + '@radix-ui/number@1.1.1': {} '@radix-ui/primitive@1.1.3': {} @@ -2441,6 +2712,9 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/trusted-types@2.0.7': + optional: true + aria-hidden@1.2.6: dependencies: tslib: 2.8.1 @@ -2477,12 +2751,24 @@ snapshots: clsx@2.1.1: {} + core-js@3.47.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + csstype@3.2.3: {} detect-libc@2.1.2: {} detect-node-es@1.1.0: {} + dompurify@3.3.1: + optionalDependencies: + '@types/trusted-types': 2.0.7 + electron-to-chromium@1.5.263: {} enhanced-resolve@5.18.3: @@ -2492,12 +2778,16 @@ snapshots: escalade@3.2.0: {} + fflate@0.4.8: {} + fraction.js@5.3.4: {} get-nonce@1.0.1: {} graceful-fs@4.2.11: {} + isexe@2.0.0: {} + jiti@2.6.1: {} lightningcss-darwin-arm64@1.30.1: @@ -2545,6 +2835,8 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.1 lightningcss-win32-x64-msvc: 1.30.1 + long@5.3.2: {} + lucide-react@0.511.0(react@19.1.2): dependencies: react: 19.1.2 @@ -2561,7 +2853,7 @@ snapshots: nanoid@3.3.11: {} - next@15.5.7(react-dom@19.1.2(react@19.1.2))(react@19.1.2): + next@15.5.7(@opentelemetry/api@1.9.0)(react-dom@19.1.2(react@19.1.2))(react@19.1.2): dependencies: '@next/env': 15.5.7 '@swc/helpers': 0.5.15 @@ -2579,6 +2871,7 @@ snapshots: '@next/swc-linux-x64-musl': 15.5.7 '@next/swc-win32-arm64-msvc': 15.5.7 '@next/swc-win32-x64-msvc': 15.5.7 + '@opentelemetry/api': 1.9.0 sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' @@ -2588,6 +2881,8 @@ snapshots: normalize-range@0.1.2: {} + path-key@3.1.1: {} + picocolors@1.1.1: {} postcss-value-parser@4.2.0: {} @@ -2604,6 +2899,41 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + posthog-js@1.321.1: + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.208.0 + '@opentelemetry/exporter-logs-otlp-http': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.3.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) + '@posthog/core': 1.9.1 + '@posthog/types': 1.321.1 + core-js: 3.47.0 + dompurify: 3.3.1 + fflate: 0.4.8 + preact: 10.28.2 + query-selector-shadow-dom: 1.0.1 + web-vitals: 4.2.4 + + preact@10.28.2: {} + + protobufjs@7.5.4: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 22.19.1 + long: 5.3.2 + + query-selector-shadow-dom@1.0.1: {} + radix-ui@1.4.3(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.2(react@19.1.2))(react@19.1.2): dependencies: '@radix-ui/primitive': 1.1.3 @@ -2738,6 +3068,12 @@ snapshots: '@img/sharp-win32-x64': 0.34.5 optional: true + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + source-map-js@1.2.1: {} styled-jsx@5.1.6(react@19.1.2): @@ -2792,6 +3128,12 @@ snapshots: dependencies: react: 19.1.2 + web-vitals@4.2.4: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 + yallist@5.0.0: {} zod@3.25.76: {} diff --git a/apps/next-js/15-pages-router-todo/posthog-setup-report.md b/apps/next-js/15-pages-router-todo/posthog-setup-report.md new file mode 100644 index 00000000..f64b34a9 --- /dev/null +++ b/apps/next-js/15-pages-router-todo/posthog-setup-report.md @@ -0,0 +1,71 @@ +# PostHog post-wizard report + +The wizard has completed a deep integration of PostHog into your Next.js 15 Pages Router Todo application. The integration includes client-side analytics initialization via the `instrumentation-client.ts` file (the recommended approach for Next.js 15.3+), automatic exception tracking, and custom event tracking for all core todo operations. A reverse proxy has been configured in `next.config.ts` to route PostHog requests through your domain, which helps avoid ad blockers and improves data collection reliability. + +## Files Created/Modified + +| File | Change | +|------|--------| +| `instrumentation-client.ts` | Created - PostHog client initialization with exception tracking | +| `next.config.ts` | Modified - Added rewrites for PostHog reverse proxy | +| `components/todos/todo-list.tsx` | Modified - Added PostHog event captures for all todo operations | +| `.env` | Created - Environment variables for PostHog API key and host | + +## Events Instrumented + +| Event Name | Description | File | +|------------|-------------|------| +| `todo_created` | Fired when a user successfully creates a new todo item | `components/todos/todo-list.tsx` | +| `todo_completed` | Fired when a user marks a todo as completed | `components/todos/todo-list.tsx` | +| `todo_uncompleted` | Fired when a user marks a completed todo as incomplete | `components/todos/todo-list.tsx` | +| `todo_deleted` | Fired when a user deletes a todo item | `components/todos/todo-list.tsx` | +| `todo_create_failed` | Fired when creating a todo fails due to an error | `components/todos/todo-list.tsx` | +| `todo_update_failed` | Fired when updating a todo fails due to an error | `components/todos/todo-list.tsx` | +| `todo_delete_failed` | Fired when deleting a todo fails due to an error | `components/todos/todo-list.tsx` | +| `todos_fetch_failed` | Fired when fetching the todos list fails due to an error | `components/todos/todo-list.tsx` | + +## Event Properties + +Each event includes relevant properties: + +- **todo_created**: `todo_id`, `has_description` +- **todo_completed/todo_uncompleted**: `todo_id` +- **todo_deleted**: `todo_id` +- **Error events**: `todo_id` (where applicable), `error` (error message) + +## Next steps + +To visualize your data in PostHog, create a dashboard with the following recommended insights: + +1. **Todo Creation Trend** - Track `todo_created` events over time to understand user engagement +2. **Task Completion Funnel** - Create a funnel from `todo_created` -> `todo_completed` to measure conversion +3. **Error Rate Monitoring** - Track all `*_failed` events to monitor application health +4. **User Engagement** - Compare `todo_created`, `todo_completed`, and `todo_deleted` to understand usage patterns +5. **Completion Rate** - Calculate the ratio of `todo_completed` to `todo_created` events + +### Creating Your Dashboard + +1. Go to your PostHog project: https://us.i.posthog.com +2. Navigate to **Dashboards** > **New Dashboard** +3. Name it "Todo App Analytics" +4. Add insights using the events listed above + +### Recommended Insight Configurations + +**Task Completion Funnel:** +- Type: Funnel +- Steps: `todo_created` -> `todo_completed` +- Date range: Last 30 days + +**Error Monitoring:** +- Type: Trends +- Events: `todo_create_failed`, `todo_update_failed`, `todo_delete_failed`, `todos_fetch_failed` +- Date range: Last 7 days + +## Configuration Details + +- **PostHog Host**: Configured via `NEXT_PUBLIC_POSTHOG_HOST` environment variable +- **API Key**: Configured via `NEXT_PUBLIC_POSTHOG_KEY` environment variable +- **Reverse Proxy**: Enabled at `/ingest` path +- **Exception Tracking**: Enabled via `capture_exceptions: true` +- **Debug Mode**: Enabled in development environment