New tab lets you search for any user by name or email, then shows: - Summary cards (cost, tokens, conversations, visits) for the period - All their conversations with titles, models, and cost breakdown - Model usage breakdown with prompt/completion split - CSV export of the full user report Two new API endpoints: /api/search-users and /api/user-detail Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
298 lines
12 KiB
HTML
298 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>LibreChat Analytics</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="css/style.css">
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
|
|
<script src="https://unpkg.com/lucide@latest"></script>
|
|
</head>
|
|
<body>
|
|
<div id="app">
|
|
<!-- Sidebar -->
|
|
<nav class="sidebar">
|
|
<div class="sidebar-header">
|
|
<svg class="logo-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/>
|
|
<path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/>
|
|
<path d="M18 12a2 2 0 0 0 0 4h4v-4Z"/>
|
|
</svg>
|
|
<span class="logo-text">Analytics</span>
|
|
</div>
|
|
<ul class="nav-links">
|
|
<li class="active" data-tab="overview">
|
|
<i data-lucide="bar-chart-3" style="width:20px;height:20px"></i>
|
|
Overview
|
|
</li>
|
|
<li data-tab="users">
|
|
<i data-lucide="users" style="width:20px;height:20px"></i>
|
|
Users
|
|
</li>
|
|
<li data-tab="models">
|
|
<i data-lucide="cpu" style="width:20px;height:20px"></i>
|
|
Models
|
|
</li>
|
|
<li data-tab="conversations">
|
|
<i data-lucide="message-circle" style="width:20px;height:20px"></i>
|
|
Conversations
|
|
</li>
|
|
<li data-tab="agents">
|
|
<i data-lucide="bot" style="width:20px;height:20px"></i>
|
|
Agents
|
|
</li>
|
|
<li data-tab="user-lookup">
|
|
<i data-lucide="search" style="width:20px;height:20px"></i>
|
|
User Lookup
|
|
</li>
|
|
</ul>
|
|
<div class="sidebar-footer">
|
|
Auto-refresh: 60s
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- Main Content -->
|
|
<main class="content">
|
|
<div class="top-bar">
|
|
<h1>LibreChat Analytics</h1>
|
|
</div>
|
|
|
|
<!-- Filter Bar -->
|
|
<div class="filter-bar">
|
|
<div class="period-selector">
|
|
<button class="period-btn active" data-period="24h">24H</button>
|
|
<button class="period-btn" data-period="7d">7D</button>
|
|
<button class="period-btn" data-period="30d">30D</button>
|
|
<button class="period-btn" data-period="custom">Custom</button>
|
|
</div>
|
|
<div class="date-range-picker" id="dateRangePicker" style="display:none">
|
|
<input type="date" id="startDate">
|
|
<span class="date-separator">to</span>
|
|
<input type="date" id="endDate">
|
|
</div>
|
|
<span class="refresh-info" id="lastRefresh"></span>
|
|
</div>
|
|
|
|
<!-- Overview Tab -->
|
|
<div class="tab-content active" id="tab-overview">
|
|
<div class="tab-title-row">
|
|
<div></div>
|
|
<button class="btn-export" onclick="exportOverview()"><i data-lucide="download" style="width:16px;height:16px"></i> Export CSV</button>
|
|
</div>
|
|
<div class="stats-grid" id="summaryCards">
|
|
<div class="stat-card">
|
|
<div class="stat-icon cost"><i data-lucide="dollar-sign"></i></div>
|
|
<div><span class="stat-label">Total Cost</span><span class="stat-value" id="totalCost">--</span></div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-icon tokens"><i data-lucide="zap"></i></div>
|
|
<div><span class="stat-label">Total Tokens</span><span class="stat-value" id="totalTokens">--</span></div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-icon users"><i data-lucide="users"></i></div>
|
|
<div><span class="stat-label">Active Users</span><span class="stat-value" id="activeUsers">--</span></div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-icon convos"><i data-lucide="message-square"></i></div>
|
|
<div><span class="stat-label">Conversations</span><span class="stat-value" id="conversations">--</span></div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-icon visits"><i data-lucide="log-in"></i></div>
|
|
<div><span class="stat-label">Visits</span><span class="stat-value" id="visits">--</span></div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-icon unique"><i data-lucide="user-check"></i></div>
|
|
<div><span class="stat-label">Unique Users</span><span class="stat-value" id="uniqueUsers">--</span></div>
|
|
</div>
|
|
</div>
|
|
<div class="charts-grid">
|
|
<div class="chart-container chart-wide">
|
|
<h3>Usage Over Time</h3>
|
|
<canvas id="usageChart"></canvas>
|
|
</div>
|
|
<div class="chart-container">
|
|
<h3>Cost by Model</h3>
|
|
<canvas id="costByModelChart"></canvas>
|
|
</div>
|
|
<div class="chart-container">
|
|
<h3>Cost Breakdown (Input vs Output)</h3>
|
|
<canvas id="costBreakdownChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Users Tab -->
|
|
<div class="tab-content" id="tab-users">
|
|
<div class="table-container">
|
|
<div class="table-header"><h3>Top Users by Cost</h3><button class="btn-export" onclick="exportUsers()"><i data-lucide="download" style="width:16px;height:16px"></i> Export CSV</button></div>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Name</th>
|
|
<th>Email</th>
|
|
<th class="text-right">Tokens</th>
|
|
<th class="text-right">Cost</th>
|
|
<th class="text-right">Conversations</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="usersTableBody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Models Tab -->
|
|
<div class="tab-content" id="tab-models">
|
|
<div class="table-container">
|
|
<div class="table-header"><h3>Top Models by Cost</h3><button class="btn-export" onclick="exportModels()"><i data-lucide="download" style="width:16px;height:16px"></i> Export CSV</button></div>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Model</th>
|
|
<th class="text-right">Prompt Tokens</th>
|
|
<th class="text-right">Completion Tokens</th>
|
|
<th class="text-right">Prompt Cost</th>
|
|
<th class="text-right">Completion Cost</th>
|
|
<th class="text-right">Total Cost</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="modelsTableBody"></tbody>
|
|
</table>
|
|
</div>
|
|
<div class="charts-grid">
|
|
<div class="chart-container chart-wide">
|
|
<h3>Model Cost Comparison (Input vs Output)</h3>
|
|
<canvas id="modelCostChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Conversations Tab -->
|
|
<div class="tab-content" id="tab-conversations">
|
|
<div class="table-container">
|
|
<div class="table-header">
|
|
<h3 id="conversationsTitle">Most Expensive Conversations</h3>
|
|
<div style="display:flex;gap:0.75rem;align-items:center">
|
|
<button class="btn-export btn-filter-user" id="clearUserFilter" onclick="clearUserFilter()" style="display:none"><i data-lucide="x" style="width:14px;height:14px"></i> Clear filter</button>
|
|
<button class="btn-export" onclick="exportConversations()"><i data-lucide="download" style="width:16px;height:16px"></i> Export CSV</button>
|
|
</div>
|
|
</div>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Title</th>
|
|
<th>User</th>
|
|
<th>Models</th>
|
|
<th class="text-right">Input Tokens</th>
|
|
<th class="text-right">Output Tokens</th>
|
|
<th class="text-right">Total Cost</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="conversationsTableBody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Agents Tab -->
|
|
<div class="tab-content" id="tab-agents">
|
|
<div class="table-container">
|
|
<div class="table-header"><h3>Top Agents by Cost</h3><button class="btn-export" onclick="exportAgents()"><i data-lucide="download" style="width:16px;height:16px"></i> Export CSV</button></div>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Agent Name</th>
|
|
<th>Underlying Model</th>
|
|
<th>Provider</th>
|
|
<th class="text-right">Tokens</th>
|
|
<th class="text-right">Cost</th>
|
|
<th class="text-right">Conversations</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="agentsTableBody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- User Lookup Tab -->
|
|
<div class="tab-content" id="tab-user-lookup">
|
|
<div class="user-search-bar">
|
|
<div class="search-input-wrap">
|
|
<i data-lucide="search" style="width:18px;height:18px"></i>
|
|
<input type="text" id="userSearchInput" placeholder="Search by name or email..." autocomplete="off">
|
|
</div>
|
|
<button class="btn-export" onclick="searchUser()"><i data-lucide="search" style="width:16px;height:16px"></i> Search</button>
|
|
</div>
|
|
<div id="userSearchResults" class="search-results" style="display:none"></div>
|
|
|
|
<div id="userDetailSection" style="display:none">
|
|
<div class="table-header" style="padding:0;border:none;margin-bottom:1.5rem">
|
|
<h3 id="userLookupTitle">--</h3>
|
|
<button class="btn-export" onclick="exportUserLookup()"><i data-lucide="download" style="width:16px;height:16px"></i> Export CSV</button>
|
|
</div>
|
|
|
|
<div class="stats-grid" id="userSummaryCards">
|
|
<div class="stat-card">
|
|
<div class="stat-icon cost"><i data-lucide="dollar-sign"></i></div>
|
|
<div><span class="stat-label">Total Cost</span><span class="stat-value" id="userTotalCost">--</span></div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-icon tokens"><i data-lucide="zap"></i></div>
|
|
<div><span class="stat-label">Total Tokens</span><span class="stat-value" id="userTotalTokens">--</span></div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-icon convos"><i data-lucide="message-square"></i></div>
|
|
<div><span class="stat-label">Conversations</span><span class="stat-value" id="userConversations">--</span></div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-icon visits"><i data-lucide="log-in"></i></div>
|
|
<div><span class="stat-label">Visits</span><span class="stat-value" id="userVisits">--</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-container">
|
|
<div class="table-header"><h3>Conversations</h3></div>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Title</th>
|
|
<th>Models</th>
|
|
<th class="text-right">Input Tokens</th>
|
|
<th class="text-right">Output Tokens</th>
|
|
<th class="text-right">Total Cost</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="userConvosTableBody"></tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="table-container">
|
|
<div class="table-header"><h3>Model Breakdown</h3></div>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Model</th>
|
|
<th class="text-right">Prompt Tokens</th>
|
|
<th class="text-right">Completion Tokens</th>
|
|
<th class="text-right">Prompt Cost</th>
|
|
<th class="text-right">Completion Cost</th>
|
|
<th class="text-right">Total Cost</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="userModelsTableBody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<script src="js/app.js"></script>
|
|
</body>
|
|
</html>
|