mirror of
https://github.com/kjanat/livedash-node.git
synced 2026-01-16 13:32:08 +01:00
Improves the dashboard with additional metrics and visualizations to provide a more comprehensive overview of application usage and performance. Adds new charts, including: - Word cloud for category analysis - Geographic map for user distribution (simulated data) - Response time distribution chart Refactors existing components for improved clarity and reusability, including the introduction of a generic `MetricCard` component. Improves error handling and user feedback during data refresh and session loading. Adds recommended VSCode extensions for ESLint and Prettier.
136 lines
3.6 KiB
TypeScript
136 lines
3.6 KiB
TypeScript
// Functions to calculate metrics over sessions
|
|
import {
|
|
ChatSession,
|
|
DayMetrics,
|
|
CategoryMetrics,
|
|
LanguageMetrics,
|
|
MetricsResult,
|
|
} from "./types";
|
|
|
|
interface CompanyConfig {
|
|
sentimentAlert?: number;
|
|
}
|
|
|
|
export function sessionMetrics(
|
|
sessions: ChatSession[],
|
|
companyConfig: CompanyConfig = {}
|
|
): MetricsResult {
|
|
const total = sessions.length;
|
|
const byDay: DayMetrics = {};
|
|
const byCategory: CategoryMetrics = {};
|
|
const byLanguage: LanguageMetrics = {};
|
|
const tokensByDay: DayMetrics = {};
|
|
const tokensCostByDay: DayMetrics = {};
|
|
|
|
let escalated = 0,
|
|
forwarded = 0;
|
|
let totalSentiment = 0,
|
|
sentimentCount = 0;
|
|
let totalResponse = 0,
|
|
responseCount = 0;
|
|
let totalTokens = 0,
|
|
totalTokensEur = 0;
|
|
|
|
// For sentiment distribution
|
|
let sentimentPositive = 0,
|
|
sentimentNegative = 0,
|
|
sentimentNeutral = 0;
|
|
|
|
// Calculate total session duration in minutes
|
|
let totalDuration = 0;
|
|
let durationCount = 0;
|
|
|
|
sessions.forEach((s) => {
|
|
const day = s.startTime.toISOString().slice(0, 10);
|
|
byDay[day] = (byDay[day] || 0) + 1;
|
|
|
|
if (s.category) byCategory[s.category] = (byCategory[s.category] || 0) + 1;
|
|
if (s.language) byLanguage[s.language] = (byLanguage[s.language] || 0) + 1;
|
|
|
|
// Process token usage by day
|
|
if (s.tokens) {
|
|
tokensByDay[day] = (tokensByDay[day] || 0) + s.tokens;
|
|
}
|
|
|
|
// Process token cost by day
|
|
if (s.tokensEur) {
|
|
tokensCostByDay[day] = (tokensCostByDay[day] || 0) + s.tokensEur;
|
|
}
|
|
|
|
if (s.endTime) {
|
|
const duration =
|
|
(s.endTime.getTime() - s.startTime.getTime()) / (1000 * 60); // minutes
|
|
totalDuration += duration;
|
|
durationCount++;
|
|
}
|
|
|
|
if (s.escalated) escalated++;
|
|
if (s.forwardedHr) forwarded++;
|
|
|
|
if (s.sentiment != null) {
|
|
totalSentiment += s.sentiment;
|
|
sentimentCount++;
|
|
|
|
// Classify sentiment
|
|
if (s.sentiment > 0.3) {
|
|
sentimentPositive++;
|
|
} else if (s.sentiment < -0.3) {
|
|
sentimentNegative++;
|
|
} else {
|
|
sentimentNeutral++;
|
|
}
|
|
}
|
|
|
|
if (s.avgResponseTime != null) {
|
|
totalResponse += s.avgResponseTime;
|
|
responseCount++;
|
|
}
|
|
|
|
totalTokens += s.tokens || 0;
|
|
totalTokensEur += s.tokensEur || 0;
|
|
});
|
|
|
|
// Now add sentiment alert logic:
|
|
let belowThreshold = 0;
|
|
const threshold = companyConfig.sentimentAlert ?? null;
|
|
if (threshold != null) {
|
|
for (const s of sessions) {
|
|
if (s.sentiment != null && s.sentiment < threshold) belowThreshold++;
|
|
}
|
|
}
|
|
|
|
// Calculate average sessions per day
|
|
const dayCount = Object.keys(byDay).length;
|
|
const avgSessionsPerDay = dayCount > 0 ? total / dayCount : 0;
|
|
|
|
// Calculate average session length
|
|
const avgSessionLength =
|
|
durationCount > 0 ? totalDuration / durationCount : null;
|
|
|
|
return {
|
|
totalSessions: total,
|
|
avgSessionsPerDay,
|
|
avgSessionLength,
|
|
days: byDay,
|
|
languages: byLanguage,
|
|
categories: byCategory,
|
|
belowThresholdCount: belowThreshold,
|
|
// Additional metrics not in the interface - using type assertion
|
|
escalatedCount: escalated,
|
|
forwardedCount: forwarded,
|
|
avgSentiment: sentimentCount ? totalSentiment / sentimentCount : undefined,
|
|
avgResponseTime: responseCount ? totalResponse / responseCount : undefined,
|
|
totalTokens,
|
|
totalTokensEur,
|
|
sentimentThreshold: threshold,
|
|
lastUpdated: Date.now(), // Add current timestamp
|
|
|
|
// New metrics for enhanced dashboard
|
|
sentimentPositiveCount: sentimentPositive,
|
|
sentimentNeutralCount: sentimentNeutral,
|
|
sentimentNegativeCount: sentimentNegative,
|
|
tokensByDay,
|
|
tokensCostByDay,
|
|
};
|
|
}
|