feat(journeys): add frontend API client and SWR hooks
This commit is contained in:
93
lib/api/journeys.ts
Normal file
93
lib/api/journeys.ts
Normal 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 ?? [])
|
||||
}
|
||||
@@ -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 }
|
||||
|
||||
Reference in New Issue
Block a user