mirror of
https://github.com/kjanat/livedash-node.git
synced 2026-01-16 08:32:09 +01:00
- Add package.json with dependencies and scripts for Next.js and Prisma - Implement API routes for session management, user authentication, and company configuration - Create database schema for Company, User, and Session models in Prisma - Set up authentication with NextAuth and JWT - Add password reset functionality and user registration endpoint - Configure Tailwind CSS and PostCSS for styling - Implement metrics and dashboard settings API endpoints
86 lines
2.8 KiB
TypeScript
86 lines
2.8 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 = {};
|
|
let escalated = 0, forwarded = 0;
|
|
let totalSentiment = 0, sentimentCount = 0;
|
|
let totalResponse = 0, responseCount = 0;
|
|
let totalTokens = 0, totalTokensEur = 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;
|
|
|
|
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++;
|
|
}
|
|
|
|
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 : null,
|
|
avgResponseTime: responseCount ? totalResponse / responseCount : null,
|
|
totalTokens,
|
|
totalTokensEur,
|
|
sentimentThreshold: threshold,
|
|
} as MetricsResult;
|
|
}
|