learning_ai_common_plat/dashboards/admin-web/src/lib/predictive-client.ts
saravanakumardb1 a31fdfe55a feat(predictive-analytics): complete admin UI for churn prediction and health scoring
- Add health-dashboard page with 6-dimension health cards and anomaly detection
- Add predictive/at-risk page with user risk profiles and segmentation
- Add predictive/campaigns page with campaign management and stats
- Add predictive-client.ts API client with full type coverage
- Update all 3 roadmaps to reflect complete implementation status
2026-03-03 13:48:37 -08:00

251 lines
7.8 KiB
TypeScript

/**
* Predictive Analytics API client for the admin dashboard.
* Churn prediction, health scoring, and retention campaigns.
*/
import { createApiClient } from '@bytelyst/api-client';
const predictiveApi = createApiClient({
baseUrl: `${process.env.PLATFORM_SERVICE_URL || 'http://localhost:4003'}/api`,
defaultHeaders: {
'x-product-id': process.env.PRODUCT_ID || 'lysnrai',
},
});
// ── Health Scoring ────────────────────────────────────────────
export interface HealthDimension {
score: number;
metrics: Record<string, number>;
trend: 'improving' | 'stable' | 'declining';
}
export interface ProductHealth {
id: string;
productId: string;
date: string;
overallHealthScore: number;
healthStatus: 'critical' | 'warning' | 'healthy';
dimensions: {
acquisition: HealthDimension;
activation: HealthDimension;
retention: HealthDimension;
engagement: HealthDimension;
revenue: HealthDimension;
stability: HealthDimension;
};
anomalies: Array<{
metric: string;
expectedValue: number;
actualValue: number;
deviationPercent: number;
severity: 'critical' | 'warning';
suggestedCause?: string;
}>;
forecasts: {
next7Days: { expectedHealthScore: number; confidenceInterval: [number, number] };
next30Days: { expectedHealthScore: number; confidenceInterval: [number, number] };
};
vsBaseline7Day: number;
vsBaseline30Day: number;
}
export async function getProductHealth(productId?: string): Promise<ProductHealth[]> {
const url = productId ? `/predictive/health?productId=${productId}` : '/predictive/health';
return predictiveApi.fetch<ProductHealth[]>(url);
}
export async function getProductHealthDetail(productId: string): Promise<ProductHealth> {
return predictiveApi.fetch<ProductHealth>(`/predictive/health/${productId}`);
}
export async function getHealthTrends(productId: string, days = 30): Promise<ProductHealth[]> {
return predictiveApi.fetch<ProductHealth[]>(`/predictive/health/${productId}/trends?days=${days}`);
}
// ── Churn Prediction ─────────────────────────────────────────
export type RiskSegment = 'critical' | 'high' | 'medium' | 'low';
export interface RiskFactor {
feature: string;
contribution: number;
direction: 'positive' | 'negative';
description: string;
}
export interface ChurnPrediction {
userId: string;
productId: string;
churnProbability: number;
riskSegment: RiskSegment;
confidenceScore: number;
modelVersion: string;
predictionTimestamp: string;
explanation: {
topRiskFactors: RiskFactor[];
nlExplanation: string;
suggestedActions: string[];
};
}
export async function getChurnScore(userId: string, productId: string, horizon = 30): Promise<ChurnPrediction> {
return predictiveApi.fetch<ChurnPrediction>('/predictive/churn-score', {
method: 'POST',
body: JSON.stringify({ userId, productId, horizon: String(horizon) }),
});
}
export interface AtRiskUser {
userId: string;
productId: string;
churnProbability: number;
riskSegment: RiskSegment;
confidenceScore: number;
daysSinceLastSession: number;
predictionTimestamp: string;
}
export async function getAtRiskUsers(options: {
productId?: string;
segment?: RiskSegment;
limit?: number;
offset?: number;
} = {}): Promise<{ users: AtRiskUser[]; total: number }> {
const params = new URLSearchParams();
if (options.productId) params.set('productId', options.productId);
if (options.segment) params.set('segment', options.segment);
if (options.limit) params.set('limit', String(options.limit));
if (options.offset) params.set('offset', String(options.offset));
return predictiveApi.fetch<{ users: AtRiskUser[]; total: number }>(`/predictive/at-risk-users?${params}`);
}
export interface UserRiskProfile extends ChurnPrediction {
interventionHistory: Array<{
action: string;
timestamp: string;
outcome?: 'responded' | 'ignored' | 'churned' | 'retained';
}>;
}
export async function getUserRiskProfile(userId: string): Promise<UserRiskProfile> {
return predictiveApi.fetch<UserRiskProfile>(`/predictive/users/${userId}/risk-profile`);
}
// ── Model Performance ─────────────────────────────────────────
export interface ModelPerformance {
modelVersion: string;
modelType: string;
trainedAt: string;
auc: number;
precisionAt10: number;
recallAt10: number;
calibrationSlope: number;
featureImportance: Array<{ feature: string; importance: number }>;
}
export async function getModelPerformance(): Promise<ModelPerformance> {
return predictiveApi.fetch<ModelPerformance>('/predictive/model/performance');
}
export async function getFeatureImportance(): Promise<Array<{ feature: string; importance: number }>> {
const res = await predictiveApi.fetch<{ features: Array<{ feature: string; importance: number }> }>('/predictive/model/features');
return res.features;
}
// ── Retention Campaigns ──────────────────────────────────────
export type CampaignStatus = 'draft' | 'active' | 'paused' | 'completed';
export type CampaignTriggerType = 'churn_risk' | 'health_score_drop' | 'behavioral' | 'scheduled';
export type CampaignChannel = 'email' | 'push' | 'in_app' | 'slack_cs';
export interface Campaign {
id: string;
productId: string;
name: string;
description: string;
status: CampaignStatus;
trigger: {
type: CampaignTriggerType;
conditions: Array<{ field: string; operator: string; value: unknown }>;
};
audience: {
riskSegments?: string[];
products?: string[];
userSegments?: string[];
excludeRecentContact?: number;
};
messages: Array<{
channel: CampaignChannel;
templateId: string;
variant?: string;
delayHours?: number;
}>;
stats: {
triggered: number;
sent: number;
opened: number;
clicked: number;
converted: number;
controlChurnRate: number;
treatmentChurnRate: number;
};
createdAt: string;
updatedAt: string;
}
export async function listCampaigns(productId?: string): Promise<Campaign[]> {
const url = productId ? `/predictive/campaigns?productId=${productId}` : '/predictive/campaigns';
return predictiveApi.fetch<Campaign[]>(url);
}
export async function getCampaign(id: string): Promise<Campaign> {
return predictiveApi.fetch<Campaign>(`/predictive/campaigns/${id}`);
}
export async function createCampaign(input: {
name: string;
description: string;
productId: string;
trigger: {
type: CampaignTriggerType;
conditions: Array<{ field: string; operator: string; value: unknown }>;
};
audience: {
riskSegments?: string[];
products?: string[];
userSegments?: string[];
excludeRecentContact?: number;
};
messages: Array<{
channel: CampaignChannel;
templateId: string;
variant?: string;
delayHours?: number;
}>;
}): Promise<Campaign> {
return predictiveApi.fetch<Campaign>('/predictive/campaigns', {
method: 'POST',
body: JSON.stringify(input),
});
}
export async function updateCampaign(id: string, updates: Partial<Campaign>): Promise<Campaign> {
return predictiveApi.fetch<Campaign>(`/predictive/campaigns/${id}`, {
method: 'PATCH',
body: JSON.stringify(updates),
});
}
export async function getCampaignStats(id: string): Promise<Campaign['stats']> {
return predictiveApi.fetch<Campaign['stats']>(`/predictive/campaigns/${id}/stats`);
}
export async function triggerCampaign(id: string, testUserId?: string): Promise<{ triggered: number }> {
return predictiveApi.fetch<{ triggered: number }>(`/predictive/campaigns/${id}/trigger`, {
method: 'POST',
body: JSON.stringify(testUserId ? { testUserId } : {}),
});
}