feat(journeys): add frontend API client and SWR hooks

This commit is contained in:
Usman Baig
2026-03-12 21:24:39 +01:00
parent 6964be9610
commit 7336f9126e
2 changed files with 146 additions and 0 deletions

93
lib/api/journeys.ts Normal file
View File

@@ -0,0 +1,93 @@
import apiRequest from './client'
// ─── Types ──────────────────────────────────────────────────────────
export interface PathTransition {
from_path: string
to_path: string
step_index: number
session_count: number
}
export interface TransitionsResponse {
transitions: PathTransition[]
total_sessions: number
}
export interface TopPath {
page_sequence: string[]
session_count: number
avg_duration: number
}
export interface EntryPoint {
path: string
session_count: number
}
// ─── Helpers ────────────────────────────────────────────────────────
function buildQuery(opts: {
startDate?: string
endDate?: string
depth?: number
limit?: number
min_sessions?: number
entry_path?: string
}): string {
const params = new URLSearchParams()
if (opts.startDate) params.append('start_date', opts.startDate)
if (opts.endDate) params.append('end_date', opts.endDate)
if (opts.depth != null) params.append('depth', opts.depth.toString())
if (opts.limit != null) params.append('limit', opts.limit.toString())
if (opts.min_sessions != null) params.append('min_sessions', opts.min_sessions.toString())
if (opts.entry_path) params.append('entry_path', opts.entry_path)
const query = params.toString()
return query ? `?${query}` : ''
}
// ─── API Functions ──────────────────────────────────────────────────
export function getJourneyTransitions(
siteId: string,
startDate?: string,
endDate?: string,
opts?: { depth?: number; minSessions?: number; entryPath?: string }
): Promise<TransitionsResponse> {
return apiRequest<TransitionsResponse>(
`/sites/${siteId}/journeys/transitions${buildQuery({
startDate,
endDate,
depth: opts?.depth,
min_sessions: opts?.minSessions,
entry_path: opts?.entryPath,
})}`
).then(r => r ?? { transitions: [], total_sessions: 0 })
}
export function getJourneyTopPaths(
siteId: string,
startDate?: string,
endDate?: string,
opts?: { limit?: number; minSessions?: number; entryPath?: string }
): Promise<TopPath[]> {
return apiRequest<{ paths: TopPath[] }>(
`/sites/${siteId}/journeys/top-paths${buildQuery({
startDate,
endDate,
limit: opts?.limit,
min_sessions: opts?.minSessions,
entry_path: opts?.entryPath,
})}`
).then(r => r?.paths ?? [])
}
export function getJourneyEntryPoints(
siteId: string,
startDate?: string,
endDate?: string
): Promise<EntryPoint[]> {
return apiRequest<{ entry_points: EntryPoint[] }>(
`/sites/${siteId}/journeys/entry-points${buildQuery({ startDate, endDate })}`
).then(r => r?.entry_points ?? [])
}

View File

@@ -17,6 +17,14 @@ import {
getDailyStats,
getBehavior,
} from '@/lib/api/stats'
import {
getJourneyTransitions,
getJourneyTopPaths,
getJourneyEntryPoints,
type TransitionsResponse,
type TopPath as JourneyTopPath,
type EntryPoint,
} from '@/lib/api/journeys'
import { listAnnotations } from '@/lib/api/annotations'
import type { Annotation } from '@/lib/api/annotations'
import { getSite } from '@/lib/api/sites'
@@ -55,6 +63,12 @@ const fetchers = {
getCampaigns(siteId, start, end, limit),
annotations: (siteId: string, start: string, end: string) => listAnnotations(siteId, start, end),
behavior: (siteId: string, start: string, end: string) => getBehavior(siteId, start, end),
journeyTransitions: (siteId: string, start: string, end: string, depth?: number, minSessions?: number, entryPath?: string) =>
getJourneyTransitions(siteId, start, end, { depth, minSessions, entryPath }),
journeyTopPaths: (siteId: string, start: string, end: string, limit?: number, minSessions?: number, entryPath?: string) =>
getJourneyTopPaths(siteId, start, end, { limit, minSessions, entryPath }),
journeyEntryPoints: (siteId: string, start: string, end: string) =>
getJourneyEntryPoints(siteId, start, end),
}
// * Standard SWR config for dashboard data
@@ -281,5 +295,44 @@ export function useBehavior(siteId: string, start: string, end: string) {
)
}
// * Hook for journey flow transitions (Sankey diagram data)
export function useJourneyTransitions(siteId: string, start: string, end: string, depth?: number, minSessions?: number, entryPath?: string) {
return useSWR<TransitionsResponse>(
siteId && start && end ? ['journeyTransitions', siteId, start, end, depth, minSessions, entryPath] : null,
() => fetchers.journeyTransitions(siteId, start, end, depth, minSessions, entryPath),
{
...dashboardSWRConfig,
refreshInterval: 60 * 1000,
dedupingInterval: 10 * 1000,
}
)
}
// * Hook for top journey paths
export function useJourneyTopPaths(siteId: string, start: string, end: string, limit?: number, minSessions?: number, entryPath?: string) {
return useSWR<JourneyTopPath[]>(
siteId && start && end ? ['journeyTopPaths', siteId, start, end, limit, minSessions, entryPath] : null,
() => fetchers.journeyTopPaths(siteId, start, end, limit, minSessions, entryPath),
{
...dashboardSWRConfig,
refreshInterval: 60 * 1000,
dedupingInterval: 10 * 1000,
}
)
}
// * Hook for journey entry points (refreshes less frequently)
export function useJourneyEntryPoints(siteId: string, start: string, end: string) {
return useSWR<EntryPoint[]>(
siteId && start && end ? ['journeyEntryPoints', siteId, start, end] : null,
() => fetchers.journeyEntryPoints(siteId, start, end),
{
...dashboardSWRConfig,
refreshInterval: 5 * 60 * 1000,
dedupingInterval: 30 * 1000,
}
)
}
// * Re-export for convenience
export { fetchers }