Conversation
Adds functionality to track outbound link clicks. This includes: - A new event type "outbound" is added to the tracking service to capture clicks on external links. - A new API endpoint is created to retrieve and display the most frequently clicked outbound links. - The UI is updated to display outbound links in both the event log and a dedicated "Outbound Links" section on the main dashboard and events page. This feature allows users to understand which external resources their visitors are interacting with, providing valuable insights into user behavior.
Implements tracking and display of outbound link clicks in user session analytics. This includes: - Adding an 'outbound' event type to the data model. - Updating the UI to display outbound click counts and details within session cards and session details views. - Renaming "Outbound Links" to "Outbound Clicks" for clarity.
|
@stijnie2210 is attempting to deploy a commit to the goldflag's projects Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughThis change introduces comprehensive support for outbound link analytics across both the client and server. It adds a new outbound event type, implements tracking, aggregation, and display of outbound link clicks, and integrates these analytics into various UI components, session summaries, and API endpoints. Related schemas, queries, and data structures are updated accordingly. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as User Interface
participant Hook as useGetOutboundLinks
participant API as /api/events/outbound/:site
participant DB as ClickHouse DB
UI->>Hook: Call useGetOutboundLinks()
Hook->>API: Fetch outbound links (site, filters, time)
API->>DB: Query outbound events for site, filters, time
DB-->>API: Return outbound event records
API-->>Hook: Return aggregated outbound links data
Hook-->>UI: Provide data (loading, links, error)
UI->>UI: Render OutboundLinksList with data
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
client/src/api/analytics/events/useGetOutboundLinks.ts (1)
21-35: LGTM with minor observation.The React Query implementation is well-structured with proper caching strategy and parameter handling. The query is correctly enabled only when a site exists, and the API call structure follows established patterns.
Note: The
filtersvariable destructured fromuseStore()on line 16 appears to be unused sincegetFilteredFilters()is called with theEVENT_FILTERSconstant. Consider whether this is intentional or if the store filters should be used instead.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
client/src/api/analytics/events/useGetOutboundLinks.ts(1 hunks)client/src/api/analytics/userSessions.ts(1 hunks)client/src/app/[site]/events/components/EventLogItem.tsx(4 hunks)client/src/app/[site]/events/components/OutboundLinksList.tsx(1 hunks)client/src/app/[site]/events/page.tsx(1 hunks)client/src/app/[site]/main/components/sections/OutboundLinks.tsx(1 hunks)client/src/app/[site]/main/page.tsx(2 hunks)client/src/components/Sessions/SessionCard.tsx(3 hunks)client/src/components/Sessions/SessionDetails.tsx(7 hunks)server/src/api/analytics/events/getEvents.ts(2 hunks)server/src/api/analytics/events/getOutboundLinks.ts(1 hunks)server/src/api/analytics/getSessions.ts(1 hunks)server/src/index.ts(3 hunks)server/src/services/tracker/trackEvent.ts(1 hunks)server/src/services/tracker/utils.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
{client,server}/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
{client,server}/**/*.{ts,tsx}: Use TypeScript with strict typing throughout both client and server
Use try/catch blocks with specific error types for error handling
Use camelCase for variables and functions, PascalCase for components and types
Group imports by external, then internal, and sort alphabetically within groups
Files:
client/src/api/analytics/userSessions.tsclient/src/app/[site]/main/page.tsxclient/src/app/[site]/events/components/EventLogItem.tsxserver/src/api/analytics/events/getOutboundLinks.tsserver/src/api/analytics/getSessions.tsserver/src/index.tsserver/src/api/analytics/events/getEvents.tsclient/src/app/[site]/events/page.tsxclient/src/api/analytics/events/useGetOutboundLinks.tsclient/src/app/[site]/main/components/sections/OutboundLinks.tsxclient/src/components/Sessions/SessionCard.tsxserver/src/services/tracker/utils.tsclient/src/app/[site]/events/components/OutboundLinksList.tsxclient/src/components/Sessions/SessionDetails.tsxserver/src/services/tracker/trackEvent.ts
client/**/*
📄 CodeRabbit Inference Engine (CLAUDE.md)
Frontend: Use Next.js, Tailwind CSS, Shadcn UI, Tanstack Query, Zustand, Luxon, Nivo, and react-hook-form
Files:
client/src/api/analytics/userSessions.tsclient/src/app/[site]/main/page.tsxclient/src/app/[site]/events/components/EventLogItem.tsxclient/src/app/[site]/events/page.tsxclient/src/api/analytics/events/useGetOutboundLinks.tsclient/src/app/[site]/main/components/sections/OutboundLinks.tsxclient/src/components/Sessions/SessionCard.tsxclient/src/app/[site]/events/components/OutboundLinksList.tsxclient/src/components/Sessions/SessionDetails.tsx
server/**/*
📄 CodeRabbit Inference Engine (CLAUDE.md)
Backend: Use Fastify, Drizzle ORM (Postgres), ClickHouse, and Zod
Files:
server/src/api/analytics/events/getOutboundLinks.tsserver/src/api/analytics/getSessions.tsserver/src/index.tsserver/src/api/analytics/events/getEvents.tsserver/src/services/tracker/utils.tsserver/src/services/tracker/trackEvent.ts
🧠 Learnings (4)
📚 Learning: applies to {client,server}/**/*.{ts,tsx} : group imports by external, then internal, and sort alphab...
Learnt from: CR
PR: rybbit-io/rybbit#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-03T17:30:25.559Z
Learning: Applies to {client,server}/**/*.{ts,tsx} : Group imports by external, then internal, and sort alphabetically within groups
Applied to files:
client/src/app/[site]/main/page.tsxserver/src/index.ts
📚 Learning: applies to client/**/* : frontend: use next.js, tailwind css, shadcn ui, tanstack query, zustand, lu...
Learnt from: CR
PR: rybbit-io/rybbit#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-03T17:30:25.559Z
Learning: Applies to client/**/* : Frontend: Use Next.js, Tailwind CSS, Shadcn UI, Tanstack Query, Zustand, Luxon, Nivo, and react-hook-form
Applied to files:
client/src/app/[site]/main/page.tsxclient/src/app/[site]/events/components/EventLogItem.tsxclient/src/app/[site]/events/page.tsxclient/src/app/[site]/main/components/sections/OutboundLinks.tsxclient/src/components/Sessions/SessionCard.tsxclient/src/app/[site]/events/components/OutboundLinksList.tsx
📚 Learning: applies to client/**/*.{tsx} : client: use react functional components with minimal useeffect and in...
Learnt from: CR
PR: rybbit-io/rybbit#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-03T17:30:25.559Z
Learning: Applies to client/**/*.{tsx} : Client: Use React functional components with minimal useEffect and inline functions
Applied to files:
client/src/app/[site]/events/components/EventLogItem.tsxclient/src/app/[site]/events/page.tsxclient/src/api/analytics/events/useGetOutboundLinks.tsclient/src/app/[site]/main/components/sections/OutboundLinks.tsxclient/src/components/Sessions/SessionCard.tsxclient/src/app/[site]/events/components/OutboundLinksList.tsxclient/src/components/Sessions/SessionDetails.tsx
📚 Learning: applies to server/**/* : backend: use fastify, drizzle orm (postgres), clickhouse, and zod...
Learnt from: CR
PR: rybbit-io/rybbit#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-03T17:30:25.559Z
Learning: Applies to server/**/* : Backend: Use Fastify, Drizzle ORM (Postgres), ClickHouse, and Zod
Applied to files:
server/src/api/analytics/events/getOutboundLinks.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build Client Images (ubuntu-latest, linux/amd64)
- GitHub Check: Build Client Images (ubuntu-24.04-arm, linux/arm64)
🔇 Additional comments (37)
client/src/api/analytics/userSessions.ts (1)
40-40: LGTM! Clean type extension for outbound events.The addition of the
outbound: numberproperty correctly extends the session response type to include outbound event counts, maintaining consistency with existing event count properties.client/src/app/[site]/main/page.tsx (2)
10-10: LGTM! Proper import organization.The import follows the coding guidelines by being properly grouped with other internal imports and maintains alphabetical ordering within the group.
38-38: LGTM! Clean integration into the dashboard layout.The OutboundLinks component is appropriately placed within the grid layout alongside other analytics sections.
server/src/api/analytics/events/getEvents.ts (2)
79-79: LGTM! Consistent outbound event type inclusion.The count query correctly includes 'outbound' events alongside existing event types for accurate pagination metadata.
115-115: LGTM! Proper outbound event filtering in main query.The main events query correctly includes 'outbound' events, maintaining consistency with the count query for proper event retrieval.
server/src/index.ts (3)
13-13: LGTM! Proper import organization.The import is correctly placed within the events-related imports group and maintains alphabetical ordering per the coding guidelines.
251-251: LGTM! Consistent analytics route registration.The outbound events route is properly added to the ANALYTICS_ROUTES array, enabling public access for sites marked as public.
338-338: LGTM! Proper route handler registration.The GET route handler follows the established pattern with correct path parameter syntax and handler function reference.
client/src/components/Sessions/SessionCard.tsx (3)
3-3: LGTM: Clean import additionThe
ExternalLinkicon import is properly added following the existing import structure.
103-111: LGTM: Consistent outbound clicks UI implementationThe outbound clicks badge follows the established pattern of other metric badges (pageviews, events, errors) with:
- Consistent styling and layout
- Appropriate ExternalLink icon with purple color theme
- Proper tooltip implementation
- Correct data formatting using the
formatterutility
210-210: LGTM: Skeleton consistency maintainedThe additional skeleton placeholder maintains visual consistency with the loading state structure.
client/src/app/[site]/events/page.tsx (2)
6-6: LGTM: Clean imports and hook usageThe new imports and hook usage follow established patterns:
- Proper import structure maintained
- Hook follows existing naming convention
- Consistent with other data fetching in the component
Also applies to: 12-12, 18-18
25-47: LGTM: Well-structured grid layoutThe two-column grid layout effectively presents both Custom Events and Outbound Clicks:
- Responsive design with
lg:grid-cols-2- Consistent card structure for both sections
- Proper prop passing to
OutboundLinksListcomponent- Maintains existing component patterns
client/src/app/[site]/events/components/EventLogItem.tsx (2)
9-9: LGTM: Clean icon import and boolean flagThe
ExternalLinkicon import andisOutboundboolean flag are properly implemented following the existing pattern for event type detection.Also applies to: 54-54
81-118: LGTM: Robust outbound event renderingThe outbound event handling is well-implemented with:
- Consistent ExternalLink icon with purple theme
- Proper URL validation from event properties
- Graceful fallback to "Outbound Link" text when URL is missing
- Clickable external links with proper
target="_blank"andrel="noopener noreferrer"- Consistent truncation and hover styles
- Appropriate purple color theming matching other outbound UI elements
server/src/services/tracker/utils.ts (1)
87-87: LGTM: Clean type extension for outbound eventsThe addition of
"outbound"to theeventTypeunion type is a minimal, backward-compatible change that properly extends the tracking system to support the new event type.client/src/app/[site]/main/components/sections/OutboundLinks.tsx (1)
1-55: LGTM: Well-structured analytics componentThe
OutboundLinkscomponent is well-implemented with:
- Clean React functional component structure following best practices
- Proper TypeScript typing with
Tabtype definition- Consistent loading state handling using
CardLoader- Appropriate data fetching with
useGetOutboundLinkshook- Clean UI layout with header labels and content separation
- Follows established component patterns from the codebase
The single-tab structure provides good extensibility for future tab additions while maintaining a consistent interface pattern.
client/src/api/analytics/events/useGetOutboundLinks.ts (3)
1-13: LGTM!The imports are properly grouped (external then internal) and the
OutboundLinktype definition is well-structured with appropriate TypeScript typing.
15-19: LGTM!The hook properly extracts necessary state from the global store following the established pattern of other analytics hooks in the codebase.
16-19: Fix missing destructuring offiltersfrom useStore.Line 19 references
filtersbut it's not destructured from theuseStore()call on line 16. This will cause a runtime error.Apply this diff to fix the missing destructuring:
- const { site, time, filters } = useStore(); + const { site, time, filters } = useStore();Wait, I see that
filtersis actually already in the destructuring. Let me re-examine... Actually, the code is correct. Thefiltersvariable is properly destructured fromuseStore()on line 16.Actually, let me look more carefully at the getFilteredFilters call - it's using EVENT_FILTERS constant, not the filters from the store. So this is correct.
server/src/api/analytics/events/getOutboundLinks.ts (4)
1-22: LGTM!The imports are properly organized and the type definitions correctly match the API contract. The
GetOutboundLinksResponsetype aligns with the client-sideOutboundLinktype.
24-43: LGTM!The function properly implements authentication and authorization checks, correctly extracts request parameters, and follows established patterns for Fastify endpoint handlers.
45-56: LGTM!The ClickHouse query is properly constructed with parameterized queries to prevent SQL injection. The filtering logic correctly targets outbound events with appropriate time and filter constraints.
58-108: LGTM with excellent error handling.The query execution and data processing implementation is well-structured:
- Proper try-catch error handling with detailed logging
- Safe JSON parsing with fallback for invalid data
- Correct aggregation logic for URL counts and timestamps
- Appropriate sorting by click count in descending order
- Comprehensive error logging for debugging
The timestamp comparison on line 85 correctly keeps the most recent timestamp using string comparison, which works for ISO timestamp formats.
client/src/components/Sessions/SessionDetails.tsx (7)
9-9: LGTM!The
ExternalLinkimport is properly added in alphabetical order within the lucide-react imports, consistent with the existing code style.
48-48: LGTM!The
isOutboundflag follows the established pattern for event type identification and enables consistent conditional rendering throughout the component.
80-82: LGTM!The purple-themed styling for outbound events is consistent with the existing pattern for other event types and provides clear visual distinction.
96-98: LGTM!The
ExternalLinkicon with purple styling is semantically appropriate for outbound events and follows the established conditional rendering pattern.
121-139: LGTM!The outbound URL rendering is well-implemented with:
- Proper external link handling with security attributes
- Consistent purple theming
- Accessible tooltip via title attribute
- Appropriate fallback text for events without URLs
- Proper URL truncation with styling constraints
171-196: LGTM!The outbound event properties rendering correctly displays
textandtargetproperties as badges, following the established pattern for custom events with consistent styling.
313-315: LGTM!The
totalOutboundcalculation properly usesuseMemoand follows the same pattern as other event count calculations in the component.server/src/services/tracker/trackEvent.ts (1)
94-138: LGTM with robust validation logic.The outbound event schema is well-implemented with:
- Proper discriminated union pattern consistent with other event types
- Comprehensive validation for outbound-specific properties
- Required URL field with proper URL format validation using
new URL()- Optional
textandtargetfields with string type validation- Strict schema to prevent additional fields
- Good error handling for invalid JSON and URL formats
The validation logic properly handles edge cases and provides clear error messages. The use of
new URL()for URL validation is appropriate and secure.client/src/app/[site]/events/components/OutboundLinksList.tsx (5)
1-78: LGTM with excellent skeleton implementation.The imports are properly organized and the memoized skeleton component provides a realistic loading experience with Pareto-distributed widths that simulate actual data patterns.
80-111: LGTM with intelligent URL truncation.The
truncateUrlfunction is well-implemented with:
- Proper URL parsing with fallback for invalid URLs
- Intelligent truncation prioritizing domain visibility
- Good error handling for URL parsing failures
- Appropriate default parameters and edge case handling
113-134: LGTM with excellent UX.The component properly handles loading and empty states with:
- Skeleton component for loading state
- Clear empty state message with semantic icons
- Helpful guidance text for users
- Good visual hierarchy
136-158: LGTM with proper data processing.The data processing correctly:
- Calculates total count for percentage visualization
- Handles timestamp conversion from SQL to local time using Luxon
- Uses proper key generation for list rendering
- Sets up percentage calculations for visual bars
159-200: LGTM with excellent interaction design.The item rendering implementation provides:
- Consistent purple theming for outbound events
- Progressive disclosure with hover effects showing percentage and relative time
- Proper external link handling with security attributes
- Responsive design supporting size variants
- Good accessibility with title attributes and semantic markup
- Smooth animations using NumberFlow component
Extends session analytics to include outbound event tracking, providing a more comprehensive view of user behavior.
Summary by CodeRabbit