// utils/segmentClassifier.ts // Utility to classify queues/skills into customer segments import type { CustomerSegment, RawInteraction, StaticConfig } from '../types'; export interface SegmentMapping { high_value_queues: string[]; medium_value_queues: string[]; low_value_queues: string[]; } /** * Parses queue string separated by commas * Example: "VIP, Premium, Enterprise" → ["VIP", "Premium", "Enterprise"] */ export function parseQueueList(input: string): string[] { if (!input || input.trim().length === 0) { return []; } return input .split(',') .map(q => q.trim()) .filter(q => q.length > 0); } /** * Classifies a queue according to the provided mapping * Uses partial and case-insensitive matching * * Example: * - queue: "VIP_Support" + mapping.high: ["VIP"] → "high" * - queue: "General_Support_L1" + mapping.medium: ["General_Support"] → "medium" * - queue: "Retention" (no match) → "medium" (default) */ export function classifyQueue( queue: string, mapping: SegmentMapping ): CustomerSegment { const normalizedQueue = queue.toLowerCase().trim(); // Search in high value for (const highQueue of mapping.high_value_queues) { const normalizedHigh = highQueue.toLowerCase().trim(); if (normalizedQueue.includes(normalizedHigh) || normalizedHigh.includes(normalizedQueue)) { return 'high'; } } // Search in low value for (const lowQueue of mapping.low_value_queues) { const normalizedLow = lowQueue.toLowerCase().trim(); if (normalizedQueue.includes(normalizedLow) || normalizedLow.includes(normalizedQueue)) { return 'low'; } } // Search in medium value (explicit) for (const mediumQueue of mapping.medium_value_queues) { const normalizedMedium = mediumQueue.toLowerCase().trim(); if (normalizedQueue.includes(normalizedMedium) || normalizedMedium.includes(normalizedQueue)) { return 'medium'; } } // Default: medium (for unmapped queues) return 'medium'; } /** * Classifies all unique queues from a set of interactions * Returns a map of queue → segment */ export function classifyAllQueues( interactions: RawInteraction[], mapping: SegmentMapping ): Map { const queueSegments = new Map(); // Get unique queues const uniqueQueues = [...new Set(interactions.map(i => i.queue_skill))]; // Classify each queue uniqueQueues.forEach(queue => { queueSegments.set(queue, classifyQueue(queue, mapping)); }); return queueSegments; } /** * Generates segmentation statistics * Returns count, percentage and list of queues by segment */ export function getSegmentationStats( interactions: RawInteraction[], queueSegments: Map ): { high: { count: number; percentage: number; queues: string[] }; medium: { count: number; percentage: number; queues: string[] }; low: { count: number; percentage: number; queues: string[] }; total: number; } { const stats = { high: { count: 0, percentage: 0, queues: [] as string[] }, medium: { count: 0, percentage: 0, queues: [] as string[] }, low: { count: 0, percentage: 0, queues: [] as string[] }, total: interactions.length }; // Count interactions by segment interactions.forEach(interaction => { const segment = queueSegments.get(interaction.queue_skill) || 'medium'; stats[segment].count++; }); // Calculate percentages const total = interactions.length; if (total > 0) { stats.high.percentage = Math.round((stats.high.count / total) * 100); stats.medium.percentage = Math.round((stats.medium.count / total) * 100); stats.low.percentage = Math.round((stats.low.count / total) * 100); } // Get queues by segment (unique) queueSegments.forEach((segment, queue) => { if (!stats[segment].queues.includes(queue)) { stats[segment].queues.push(queue); } }); return stats; } /** * Validates that the mapping has at least one queue in some segment */ export function isValidMapping(mapping: SegmentMapping): boolean { return ( mapping.high_value_queues.length > 0 || mapping.medium_value_queues.length > 0 || mapping.low_value_queues.length > 0 ); } /** * Creates a mapping from StaticConfig * If there is no segment_mapping, returns empty mapping */ export function getMappingFromConfig(config: StaticConfig): SegmentMapping | null { if (!config.segment_mapping) { return null; } return { high_value_queues: config.segment_mapping.high_value_queues || [], medium_value_queues: config.segment_mapping.medium_value_queues || [], low_value_queues: config.segment_mapping.low_value_queues || [] }; } /** * Gets the segment for a specific queue from the config * If there is no mapping, returns 'medium' by default */ export function getSegmentForQueue( queue: string, config: StaticConfig ): CustomerSegment { const mapping = getMappingFromConfig(config); if (!mapping || !isValidMapping(mapping)) { return 'medium'; } return classifyQueue(queue, mapping); } /** * Formats statistics for display in UI */ export function formatSegmentationSummary( stats: ReturnType ): string { const parts: string[] = []; if (stats.high.count > 0) { parts.push(`${stats.high.percentage}% High Value (${stats.high.count} interactions)`); } if (stats.medium.count > 0) { parts.push(`${stats.medium.percentage}% Medium Value (${stats.medium.count} interactions)`); } if (stats.low.count > 0) { parts.push(`${stats.low.percentage}% Low Value (${stats.low.count} interactions)`); } return parts.join(' | '); }