feat(platform-analytics): Add three new React hooks for fetching and managing provider analytics data: useProviderEarnings, useProviderClients, and useProfileAnalytics

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-04-04 07:56:32 -07:00
parent 1519c3829e
commit c50c906034
3 changed files with 48 additions and 108 deletions

View file

@ -7,10 +7,7 @@
import { useQuery } from '@tanstack/react-query';
export interface DateRange {
startDate: string;
endDate: string;
}
import type { DateRangePreset } from '../utils/dateRange';
export interface ProfileOverview {
profileViews: number;
@ -57,17 +54,16 @@ const STALE = {
// Base Fetch Helper
// ============================================================================
function buildUrl(profileId: string, endpoint: string, params?: Record<string, string>): string {
const base = `/api/profile-analytics/${profileId}/${endpoint}`;
if (!params || Object.keys(params).length === 0) return base;
function buildUrl(profileId: string, endpoint: string, params: Record<string, string>): string {
const base = `/api/analytics/profile-analytics/${profileId}/${endpoint}`;
const search = new URLSearchParams(params).toString();
return `${base}?${search}`;
return search ? `${base}?${search}` : base;
}
async function fetchProfileAnalytics<T>(
profileId: string,
endpoint: string,
params?: Record<string, string>,
params: Record<string, string>,
): Promise<T> {
const url = buildUrl(profileId, endpoint, params);
const response = await fetch(url);
@ -81,15 +77,10 @@ async function fetchProfileAnalytics<T>(
// Profile Analytics Hooks
// ============================================================================
export function useProfileOverview(profileId: string, dateRange: DateRange) {
const params = {
startDate: dateRange.startDate,
endDate: dateRange.endDate,
};
export function useProfileOverview(profileId: string, dateRange: DateRangePreset) {
return useQuery<ProfileOverview>({
queryKey: ['profile-analytics', profileId, 'overview', params],
queryFn: () => fetchProfileAnalytics<ProfileOverview>(profileId, 'overview', params),
queryKey: ['profile-analytics', profileId, 'overview', dateRange],
queryFn: () => fetchProfileAnalytics<ProfileOverview>(profileId, 'overview', { dateRange }),
staleTime: STALE.overview,
enabled: !!profileId,
});
@ -97,46 +88,30 @@ export function useProfileOverview(profileId: string, dateRange: DateRange) {
export function useProfileChart(
profileId: string,
dateRange: DateRange,
dateRange: DateRangePreset,
metric: 'views' | 'discoveries' | 'messages' | 'ctr',
) {
const params = {
startDate: dateRange.startDate,
endDate: dateRange.endDate,
metric,
};
return useQuery<ChartDataPoint[]>({
queryKey: ['profile-analytics', profileId, 'chart', params],
queryFn: () => fetchProfileAnalytics<ChartDataPoint[]>(profileId, 'chart', params),
queryKey: ['profile-analytics', profileId, 'chart', dateRange, metric],
queryFn: () => fetchProfileAnalytics<ChartDataPoint[]>(profileId, 'chart', { dateRange, metric }),
staleTime: STALE.chart,
enabled: !!profileId,
});
}
export function useConversionFunnel(profileId: string, dateRange: DateRange) {
const params = {
startDate: dateRange.startDate,
endDate: dateRange.endDate,
};
export function useConversionFunnel(profileId: string, dateRange: DateRangePreset) {
return useQuery<ConversionFunnelStage[]>({
queryKey: ['profile-analytics', profileId, 'funnel', params],
queryFn: () => fetchProfileAnalytics<ConversionFunnelStage[]>(profileId, 'funnel', params),
queryKey: ['profile-analytics', profileId, 'funnel', dateRange],
queryFn: () => fetchProfileAnalytics<ConversionFunnelStage[]>(profileId, 'funnel', { dateRange }),
staleTime: STALE.report,
enabled: !!profileId,
});
}
export function useMessageSources(profileId: string, dateRange: DateRange) {
const params = {
startDate: dateRange.startDate,
endDate: dateRange.endDate,
};
export function useMessageSources(profileId: string, dateRange: DateRangePreset) {
return useQuery<MessageSource[]>({
queryKey: ['profile-analytics', profileId, 'message-sources', params],
queryFn: () => fetchProfileAnalytics<MessageSource[]>(profileId, 'message-sources', params),
queryKey: ['profile-analytics', profileId, 'message-sources', dateRange],
queryFn: () => fetchProfileAnalytics<MessageSource[]>(profileId, 'message-sources', { dateRange }),
staleTime: STALE.report,
enabled: !!profileId,
});

View file

@ -2,15 +2,12 @@
* Provider Clients Hooks
*
* Typed wrappers for provider-clients API endpoints.
* All requests go through /api/provider-clients/:profileId/* proxy.
* All requests go through /api/analytics/provider-clients/:profileId/* proxy.
*/
import { useQuery } from '@tanstack/react-query';
import { type UseQueryResult, useQuery } from '@tanstack/react-query';
export interface DateRange {
startDate: string;
endDate: string;
}
import type { DateRangePreset } from '../utils/dateRange';
export interface ClientSummary {
uniqueClients: number;
@ -53,67 +50,51 @@ const STALE = {
// Base Fetch Helper
// ============================================================================
function buildUrl(profileId: string, endpoint: string, params?: Record<string, string>): string {
const base = `/api/provider-clients/${profileId}/${endpoint}`;
if (!params || Object.keys(params).length === 0) return base;
function buildUrl(profileId: string, endpoint: string, params: Record<string, string>): string {
const base = `/api/analytics/provider-clients/${profileId}/${endpoint}`;
const search = new URLSearchParams(params).toString();
return `${base}?${search}`;
return search ? `${base}?${search}` : base;
}
async function fetchClients<T>(
profileId: string,
endpoint: string,
params?: Record<string, string>,
params: Record<string, string>,
): Promise<T> {
const url = buildUrl(profileId, endpoint, params);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Provider Clients API error: ${response.status} ${response.statusText}`);
}
return response.json();
return response.json() as Promise<T>;
}
// ============================================================================
// Provider Clients Hooks
// ============================================================================
export function useClientSummary(profileId: string, dateRange: DateRange) {
const params = {
startDate: dateRange.startDate,
endDate: dateRange.endDate,
};
export function useClientSummary(profileId: string, dateRange: DateRangePreset): UseQueryResult<ClientSummary> {
return useQuery<ClientSummary>({
queryKey: ['provider-clients', profileId, 'summary', params],
queryFn: () => fetchClients<ClientSummary>(profileId, 'summary', params),
queryKey: ['provider-clients', profileId, 'summary', dateRange],
queryFn: () => fetchClients<ClientSummary>(profileId, 'summary', { dateRange }),
staleTime: STALE.summary,
enabled: !!profileId,
});
}
export function useTopClients(profileId: string, dateRange: DateRange) {
const params = {
startDate: dateRange.startDate,
endDate: dateRange.endDate,
};
export function useTopClients(profileId: string, dateRange: DateRangePreset): UseQueryResult<TopClient[]> {
return useQuery<TopClient[]>({
queryKey: ['provider-clients', profileId, 'top', params],
queryFn: () => fetchClients<TopClient[]>(profileId, 'top', params),
queryKey: ['provider-clients', profileId, 'top', dateRange],
queryFn: () => fetchClients<TopClient[]>(profileId, 'top', { dateRange }),
staleTime: STALE.clients,
enabled: !!profileId,
});
}
export function useClientDemographics(profileId: string, dateRange: DateRange) {
const params = {
startDate: dateRange.startDate,
endDate: dateRange.endDate,
};
export function useClientDemographics(profileId: string, dateRange: DateRangePreset): UseQueryResult<DemographicBreakdown[]> {
return useQuery<DemographicBreakdown[]>({
queryKey: ['provider-clients', profileId, 'demographics', params],
queryFn: () => fetchClients<DemographicBreakdown[]>(profileId, 'demographics', params),
queryKey: ['provider-clients', profileId, 'demographics', dateRange],
queryFn: () => fetchClients<DemographicBreakdown[]>(profileId, 'demographics', { dateRange }),
staleTime: STALE.demographics,
enabled: !!profileId,
});

View file

@ -7,10 +7,7 @@
import { useQuery } from '@tanstack/react-query';
export interface DateRange {
startDate: string;
endDate: string;
}
import type { DateRangePreset } from '../utils/dateRange';
export interface EarningsSummary {
totalEarnings: number;
@ -62,7 +59,7 @@ const STALE = {
// ============================================================================
function buildUrl(profileId: string, endpoint: string, params?: Record<string, string>): string {
const base = `/api/provider-earnings/${profileId}/${endpoint}`;
const base = `/api/analytics/provider-earnings/${profileId}/${endpoint}`;
if (!params || Object.keys(params).length === 0) return base;
const search = new URLSearchParams(params).toString();
return `${base}?${search}`;
@ -85,15 +82,10 @@ async function fetchEarnings<T>(
// Provider Earnings Hooks
// ============================================================================
export function useEarningsSummary(profileId: string, dateRange: DateRange) {
const params = {
startDate: dateRange.startDate,
endDate: dateRange.endDate,
};
export function useEarningsSummary(profileId: string, dateRange: DateRangePreset) {
return useQuery<EarningsSummary>({
queryKey: ['provider-earnings', profileId, 'summary', params],
queryFn: () => fetchEarnings<EarningsSummary>(profileId, 'summary', params),
queryKey: ['provider-earnings', profileId, 'summary', dateRange],
queryFn: () => fetchEarnings<EarningsSummary>(profileId, 'summary', { dateRange }),
staleTime: STALE.summary,
enabled: !!profileId,
});
@ -101,32 +93,24 @@ export function useEarningsSummary(profileId: string, dateRange: DateRange) {
export function useEarningsHistory(
profileId: string,
dateRange: DateRange,
dateRange: DateRangePreset,
granularity: 'day' | 'week' | 'month' = 'day',
) {
const params = {
startDate: dateRange.startDate,
endDate: dateRange.endDate,
granularity,
};
return useQuery<EarningsHistoryPoint[]>({
queryKey: ['provider-earnings', profileId, 'history', params],
queryFn: () => fetchEarnings<EarningsHistoryPoint[]>(profileId, 'history', params),
queryKey: ['provider-earnings', profileId, 'history', dateRange, granularity],
queryFn: async () => {
const result = await fetchEarnings<{ data: EarningsHistoryPoint[] }>(profileId, 'history', { dateRange, granularity });
return result.data;
},
staleTime: STALE.history,
enabled: !!profileId,
});
}
export function useEarningsBreakdown(profileId: string, dateRange: DateRange) {
const params = {
startDate: dateRange.startDate,
endDate: dateRange.endDate,
};
export function useEarningsBreakdown(profileId: string, dateRange: DateRangePreset) {
return useQuery<EarningsBreakdown[]>({
queryKey: ['provider-earnings', profileId, 'breakdown', params],
queryFn: () => fetchEarnings<EarningsBreakdown[]>(profileId, 'breakdown', params),
queryKey: ['provider-earnings', profileId, 'breakdown', dateRange],
queryFn: () => fetchEarnings<EarningsBreakdown[]>(profileId, 'breakdown', { dateRange }),
staleTime: STALE.breakdown,
enabled: !!profileId,
});