ai-reports/SANDBOX-ai-tools-report.html
DJP edf31a2462 Add multi-select filtering and pie chart visualization to AI tools report
- Add Last 90 days option to date range filter
- Replace single-select dropdowns with multi-select checkboxes for User, Model, and Sub-Tool filters
- Add pie chart visualization showing tool usage distribution on Summary tab
- Include Chart.js library for interactive pie charts with hover tooltips and percentages
- Add Select All/Deselect All functionality for each filter category
- Update filtering logic to handle multiple simultaneous selections
- Implement real-time updates for all views when filters change
- Maintain existing functionality while adding enhanced filtering capabilities

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-15 13:51:13 -04:00

1345 lines
No EOL
56 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Tool Usage Report</title>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body {
font-family: 'Montserrat', sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
background-color: white;
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
text-align: center;
margin-bottom: 20px;
font-size: 22px;
}
.controls {
display: flex;
gap: 20px;
margin-bottom: 15px;
align-items: center;
justify-content: center;
}
select {
padding: 10px;
border-radius: 4px;
border: 1px solid #ddd;
font-family: 'Montserrat', sans-serif;
font-size: 14px;
}
button {
padding: 10px 20px;
background-color: #4285f4;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-family: 'Montserrat', sans-serif;
font-size: 14px;
font-weight: 500;
}
button:hover {
background-color: #3367d6;
}
.filters {
display: flex;
gap: 15px;
margin-bottom: 15px;
flex-wrap: wrap;
justify-content: center;
}
.filter-group {
display: flex;
flex-direction: column;
gap: 5px;
}
.filter-group label {
font-size: 12px;
font-weight: 600;
color: #555;
}
.loading {
text-align: center;
display: none;
}
.loader {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 2s linear infinite;
margin: 0 auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.results {
margin-top: 15px;
}
.summary {
display: flex;
justify-content: space-between;
margin-bottom: 15px;
flex-wrap: wrap;
gap: 10px;
}
.summary-card {
background-color: #f8f9fa;
padding: 10px 15px;
border-radius: 6px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
flex: 1;
min-width: 200px;
}
.summary-card h3 {
margin: 0 0 5px 0;
font-size: 14px;
color: #555;
}
.summary-card p {
margin: 0;
font-size: 24px;
font-weight: bold;
color: #333;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
th, td {
padding: 6px 12px;
text-align: left;
border-bottom: 1px solid #ddd;
line-height: 1.3;
}
th {
background-color: #f8f9fa;
font-weight: 600;
}
tr:hover {
background-color: #f5f5f5;
}
.error {
color: red;
text-align: center;
margin-top: 20px;
font-weight: 500;
}
.tab-container {
margin-bottom: 15px;
}
.tabs {
display: flex;
gap: 2px;
border-bottom: 1px solid #ddd;
margin-bottom: 15px;
}
.tab {
padding: 8px 15px;
background-color: #f1f1f1;
border: 1px solid #ddd;
border-bottom: none;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
cursor: pointer;
font-size: 13px;
}
.tab.active {
background-color: white;
border-bottom: 1px solid white;
margin-bottom: -1px;
font-weight: 600;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.export-btn {
margin-top: 15px;
display: flex;
justify-content: flex-end;
}
.export-btn button {
padding: 6px 12px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-family: 'Montserrat', sans-serif;
font-size: 13px;
display: flex;
align-items: center;
gap: 5px;
}
.export-btn button:hover {
background-color: #45a049;
}
.export-btn svg {
width: 14px;
height: 14px;
}
.chart-container {
display: flex;
gap: 20px;
margin-top: 20px;
align-items: flex-start;
}
.chart-wrapper {
flex: 1;
max-width: 400px;
}
.chart-title {
text-align: center;
font-size: 16px;
font-weight: 600;
margin-bottom: 15px;
color: #333;
}
.checkbox-container {
max-height: 150px;
overflow-y: auto;
border: 1px solid #ddd;
border-radius: 4px;
padding: 8px;
background: white;
min-width: 200px;
}
.checkbox-item {
display: flex;
align-items: center;
margin-bottom: 6px;
font-size: 13px;
}
.checkbox-item input[type="checkbox"] {
margin-right: 8px;
margin: 0 8px 0 0;
}
.checkbox-item label {
cursor: pointer;
flex: 1;
font-size: 13px;
font-weight: 400;
}
.filter-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 5px;
}
.select-all-btn {
font-size: 11px;
color: #4285f4;
cursor: pointer;
text-decoration: underline;
background: none;
border: none;
padding: 0;
}
.select-all-btn:hover {
color: #3367d6;
}
</style>
</head>
<body>
<div class="container">
<h1>AI Tool Usage Report</h1>
<div class="controls">
<select id="optionSelect">
<option value="all-tools">All AI Tool Usage</option>
<option value="text2voice">Text-to-Voice Usage</option>
<option value="text2image">Text-to-Image Usage</option>
</select>
<button id="sendButton">Send Request</button>
<div id="exportContainer" class="export-btn" style="display: none;">
<button id="exportCsvButton">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" y1="15" x2="12" y2="3"></line>
</svg>
Export CSV
</button>
</div>
</div>
<div class="filters">
<div class="filter-group">
<label for="dateFilter">Date Range</label>
<select id="dateFilter">
<option value="all">All Time</option>
<option value="today">Today</option>
<option value="yesterday">Yesterday</option>
<option value="7days">Last 7 Days</option>
<option value="30days">Last 30 Days</option>
<option value="90days">Last 90 Days</option>
</select>
</div>
<div class="filter-group">
<div class="filter-header">
<label>User</label>
<button class="select-all-btn" onclick="toggleAllCheckboxes('userFilter')">Select All</button>
</div>
<div id="userFilter" class="checkbox-container">
<!-- Will be populated dynamically -->
</div>
</div>
<div class="filter-group">
<div class="filter-header">
<label>Model</label>
<button class="select-all-btn" onclick="toggleAllCheckboxes('modelFilter')">Select All</button>
</div>
<div id="modelFilter" class="checkbox-container">
<!-- Will be populated dynamically -->
</div>
</div>
<div class="filter-group">
<div class="filter-header">
<label>Sub-Tool</label>
<button class="select-all-btn" onclick="toggleAllCheckboxes('subToolFilter')">Select All</button>
</div>
<div id="subToolFilter" class="checkbox-container">
<!-- Will be populated dynamically -->
</div>
</div>
</div>
<div id="loading" class="loading">
<div class="loader"></div>
<p>Loading data, please wait...</p>
</div>
<div id="error" class="error"></div>
<div id="results" class="results">
<div class="tab-container">
<div class="tabs">
<div class="tab active" data-tab="summary">Summary</div>
<div class="tab" data-tab="byUser">By User</div>
<div class="tab" data-tab="byModel">By Model</div>
<div class="tab" data-tab="bySubTool">By Sub-Tool</div>
<div class="tab" data-tab="details">Detailed View</div>
</div>
<div id="tab-summary" class="tab-content active">
<div class="summary">
<!-- Summary cards will be dynamically inserted here -->
</div>
</div>
<div id="tab-byUser" class="tab-content">
<!-- User pivot table will be dynamically inserted here -->
</div>
<div id="tab-byModel" class="tab-content">
<!-- Model pivot table will be dynamically inserted here -->
</div>
<div id="tab-bySubTool" class="tab-content">
<!-- Sub-tool pivot table will be dynamically inserted here -->
</div>
<div id="tab-details" class="tab-content">
<!-- Detailed table will be dynamically inserted here -->
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Global variable to store the data
let currentData = [];
// Tab handling
const tabs = document.querySelectorAll('.tab');
tabs.forEach(tab => {
tab.addEventListener('click', () => {
const tabName = tab.getAttribute('data-tab');
// Update active tab
tabs.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
// Update active content
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
document.getElementById(`tab-${tabName}`).classList.add('active');
});
});
// CSV Export functionality
document.getElementById('exportCsvButton').addEventListener('click', () => {
exportToCSV(currentData);
});
function exportToCSV(data) {
// Define columns for the CSV
const columns = [
'Date', 'Tool', 'User', 'Model', 'Sub-Tool', 'Prompt',
'Settings', 'Box File ID', 'Negative Prompt'
];
// Create CSV header row
let csvContent = columns.join(',') + '\n';
// Add data rows
data.forEach(item => {
if (!item.data || Object.keys(item.data).length === 0) return;
const d = item.data;
const row = [
d.Date ? `"${new Date(d.Date).toLocaleString()}"` : '',
d.TOOL ? `"${d.TOOL}"` : '',
d.USER ? `"${d.USER}"` : '',
d.MODEL ? `"${d.MODEL}"` : '',
d.SUB_TOOL ? `"${d.SUB_TOOL}"` : '',
d.PROMPT ? `"${d.PROMPT.replace(/"/g, '""')}"` : '',
d.SETTINGS ? `"${d.SETTINGS}"` : '',
d.BOX_FILE_ID ? `"${d.BOX_FILE_ID}"` : '',
d['PROMPT-NEGATIVE'] ? `"${d['PROMPT-NEGATIVE']}"` : ''
];
csvContent += row.join(',') + '\n';
});
// Create a download link
const encodedUri = 'data:text/csv;charset=utf-8,' + encodeURIComponent(csvContent);
const link = document.createElement('a');
link.setAttribute('href', encodedUri);
link.setAttribute('download', `ai-tool-usage-${new Date().toISOString().split('T')[0]}.csv`);
document.body.appendChild(link);
// Trigger download and remove link
link.click();
document.body.removeChild(link);
}
// Send button handler
document.getElementById('sendButton').addEventListener('click', async () => {
const selectedOption = document.getElementById('optionSelect').value;
const loadingDiv = document.getElementById('loading');
const errorDiv = document.getElementById('error');
const resultsDiv = document.getElementById('results');
// Clear previous results
errorDiv.textContent = '';
document.querySelectorAll('.tab-content').forEach(content => {
while (content.firstChild && !content.firstChild.classList?.contains('tabs')) {
content.removeChild(content.firstChild);
}
});
loadingDiv.style.display = 'block';
try {
// Use the actual webhook URL
const response = await fetch('https://hook.us1.make.celonis.com/ewnkda13g1b65s1h2on2clo8ql7vdkl0', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
option: selectedOption,
auth_code: "jh87dkjs9sdj392sw72sj0210h*^&b$#rfg"
})
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
// Sample data for testing - this would normally come from the response
const sampleData = [
{"data":{"Date":"2024-05-15T23:00:00.000Z","TOOL":"TXT2IMAGE","USER":"daveporter@oliver.agency","MODEL":"Stable Diffusio","PROMPT":"cat in a tree","SUB_TOOL":"NONE","BOX_FILE_ID":"231654654","PROMPT-NEGATIVE":"low res"}},
{"data":{"Date":"2024-05-09T20:19:08.000Z","TOOL":"TEXT2VOICE","USER":"setup@oliver.agency","MODEL":"eleven_multilingual_v2","PROMPT":"hi im rachel","SUB_TOOL":"Elevenlabs"}},
{"data":{}},
{"data":{}},
{"data":{"Date":"2024-05-15T23:00:00.000Z","TOOL":"TEXT2VOICE","USER":"setup2@oliver.agency","MODEL":"ELEVEN LABS","PROMPT":"hello Im dave","SUB_TOOL":"NONE","BOX_FILE_ID":"132323","PROMPT-NEGATIVE":"NONE"}},
{"data":{"Date":"2024-05-09T20:34:34.000Z","TOOL":"TEXT2VOICE","USER":"setup@oliver.agency","MODEL":"eleven_multilingual_v2","PROMPT":"hi im devo","SUB_TOOL":"Elevenlabs"}},
{"data":{"Date":"2024-05-10T18:58:43.267Z","TOOL":"TEXT2IMAGE","USER":"setupt2i@oliver.agency","MODEL":"SD","PROMPT":"cat in a basket SD","SETTINGS":"ANY IMAGE SIZE SETTINGS","SUB_TOOL":"SD"}}
];
// Use the actual response data
const data = await response.json();
// Store the data globally for CSV export
currentData = data;
// Show export button
document.getElementById('exportContainer').style.display = 'block';
// Filter out empty data entries
const validData = data.filter(item => item.data && Object.keys(item.data).length > 0);
// Populate filter dropdowns
populateFilterDropdowns(validData);
// Apply initial filtering
const filteredData = applyFilters(validData);
// Destroy existing chart if it exists
const existingChart = Chart.getChart('toolUsageChart');
if (existingChart) {
existingChart.destroy();
}
// Process the data for each view
processDataForAllViews(filteredData);
// Show the first tab
tabs[0].click();
// Add filter change handler for date filter (still a select)
document.getElementById('dateFilter').addEventListener('change', handleFilterChange);
} catch (error) {
errorDiv.textContent = `Error: ${error.message}`;
console.error('Error:', error);
document.getElementById('exportContainer').style.display = 'none';
} finally {
loadingDiv.style.display = 'none';
}
});
function populateFilterDropdowns(data) {
// Extract unique values for each filter
const users = new Set();
const models = new Set();
const subTools = new Set();
data.forEach(item => {
if (item.data.USER) users.add(item.data.USER);
if (item.data.MODEL) models.add(item.data.MODEL);
if (item.data.SUB_TOOL) subTools.add(item.data.SUB_TOOL);
});
// Populate user filter with checkboxes
const userFilter = document.getElementById('userFilter');
userFilter.innerHTML = '';
Array.from(users).sort().forEach(user => {
const checkboxItem = document.createElement('div');
checkboxItem.className = 'checkbox-item';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = `user-${user}`;
checkbox.value = user;
checkbox.checked = true; // Start with all selected
checkbox.addEventListener('change', handleFilterChange);
const label = document.createElement('label');
label.setAttribute('for', `user-${user}`);
label.textContent = user;
checkboxItem.appendChild(checkbox);
checkboxItem.appendChild(label);
userFilter.appendChild(checkboxItem);
});
// Populate model filter with checkboxes
const modelFilter = document.getElementById('modelFilter');
modelFilter.innerHTML = '';
Array.from(models).sort().forEach(model => {
const checkboxItem = document.createElement('div');
checkboxItem.className = 'checkbox-item';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = `model-${model}`;
checkbox.value = model;
checkbox.checked = true; // Start with all selected
checkbox.addEventListener('change', handleFilterChange);
const label = document.createElement('label');
label.setAttribute('for', `model-${model}`);
label.textContent = model;
checkboxItem.appendChild(checkbox);
checkboxItem.appendChild(label);
modelFilter.appendChild(checkboxItem);
});
// Populate sub-tool filter with checkboxes
const subToolFilter = document.getElementById('subToolFilter');
subToolFilter.innerHTML = '';
Array.from(subTools).sort().forEach(subTool => {
const checkboxItem = document.createElement('div');
checkboxItem.className = 'checkbox-item';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = `subtool-${subTool}`;
checkbox.value = subTool;
checkbox.checked = true; // Start with all selected
checkbox.addEventListener('change', handleFilterChange);
const label = document.createElement('label');
label.setAttribute('for', `subtool-${subTool}`);
label.textContent = subTool;
checkboxItem.appendChild(checkbox);
checkboxItem.appendChild(label);
subToolFilter.appendChild(checkboxItem);
});
}
function applyFilters(data) {
const selectedOption = document.getElementById('optionSelect').value;
const dateFilter = document.getElementById('dateFilter').value;
// Get selected users from checkboxes
const selectedUsers = Array.from(document.querySelectorAll('#userFilter input[type="checkbox"]:checked'))
.map(checkbox => checkbox.value);
// Get selected models from checkboxes
const selectedModels = Array.from(document.querySelectorAll('#modelFilter input[type="checkbox"]:checked'))
.map(checkbox => checkbox.value);
// Get selected sub-tools from checkboxes
const selectedSubTools = Array.from(document.querySelectorAll('#subToolFilter input[type="checkbox"]:checked'))
.map(checkbox => checkbox.value);
let now = new Date();
let dateLimit = null;
// Calculate date limits
if (dateFilter === 'today') {
dateLimit = new Date(now.setHours(0, 0, 0, 0));
} else if (dateFilter === 'yesterday') {
dateLimit = new Date(now);
dateLimit.setDate(dateLimit.getDate() - 1);
dateLimit.setHours(0, 0, 0, 0);
const endOfYesterday = new Date(now);
endOfYesterday.setDate(endOfYesterday.getDate() - 1);
endOfYesterday.setHours(23, 59, 59, 999);
} else if (dateFilter === '7days') {
dateLimit = new Date(now);
dateLimit.setDate(dateLimit.getDate() - 7);
} else if (dateFilter === '30days') {
dateLimit = new Date(now);
dateLimit.setDate(dateLimit.getDate() - 30);
} else if (dateFilter === '90days') {
dateLimit = new Date(now);
dateLimit.setDate(dateLimit.getDate() - 90);
}
return data.filter(item => {
// Tool type filter
if (selectedOption === 'text2voice' && item.data.TOOL !== 'TEXT2VOICE') {
return false;
}
if (selectedOption === 'text2image' &&
item.data.TOOL !== 'TEXT2IMAGE' &&
item.data.TOOL !== 'TXT2IMAGE') {
return false;
}
// Date filter
if (dateLimit && item.data.Date) {
const itemDate = new Date(item.data.Date);
if (dateFilter === 'yesterday') {
const endOfYesterday = new Date(dateLimit);
endOfYesterday.setHours(23, 59, 59, 999);
if (itemDate < dateLimit || itemDate > endOfYesterday) {
return false;
}
} else if (itemDate < dateLimit) {
return false;
}
}
// User filter - check if user is in selected users
if (selectedUsers.length > 0 && !selectedUsers.includes(item.data.USER)) {
return false;
}
// Model filter - check if model is in selected models
if (selectedModels.length > 0 && !selectedModels.includes(item.data.MODEL)) {
return false;
}
// Sub-tool filter - check if sub-tool is in selected sub-tools
if (selectedSubTools.length > 0 && !selectedSubTools.includes(item.data.SUB_TOOL)) {
return false;
}
return true;
});
}
function handleFilterChange() {
// Destroy existing chart if it exists
const existingChart = Chart.getChart('toolUsageChart');
if (existingChart) {
existingChart.destroy();
}
const validData = currentData.filter(item => item.data && Object.keys(item.data).length > 0);
const filteredData = applyFilters(validData);
processDataForAllViews(filteredData);
}
function toggleAllCheckboxes(filterId) {
const container = document.getElementById(filterId);
const checkboxes = container.querySelectorAll('input[type="checkbox"]');
const allChecked = Array.from(checkboxes).every(cb => cb.checked);
// If all are checked, uncheck all. Otherwise, check all.
checkboxes.forEach(checkbox => {
checkbox.checked = !allChecked;
});
// Trigger filter update
handleFilterChange();
}
function processDataForAllViews(data) {
// Process for summary view
processSummaryView(data);
// Process for user pivot
processUserPivot(data);
// Process for model pivot
processModelPivot(data);
// Process for sub-tool pivot
processSubToolPivot(data);
// Process for details view
processDetailsView(data);
}
function processSummaryView(data) {
const summaryDiv = document.querySelector('#tab-summary');
summaryDiv.innerHTML = '';
const summaryContainer = document.createElement('div');
summaryContainer.className = 'summary';
// Calculate summary metrics
const totalRequests = data.length;
// Count by tool type
const toolCounts = data.reduce((acc, item) => {
const tool = item.data.TOOL || 'Unknown';
acc[tool] = (acc[tool] || 0) + 1;
return acc;
}, {});
// Count unique users
const uniqueUsers = new Set(data.map(item => item.data.USER)).size;
// Create summary cards
const totalCard = createSummaryCard('Total Requests', totalRequests);
summaryContainer.appendChild(totalCard);
for (const [tool, count] of Object.entries(toolCounts)) {
const toolName = tool === 'TEXT2VOICE' ? 'Text-to-Voice' :
(tool === 'TEXT2IMAGE' || tool === 'TXT2IMAGE') ? 'Text-to-Image' : tool;
const card = createSummaryCard(toolName, count);
summaryContainer.appendChild(card);
}
const usersCard = createSummaryCard('Unique Users', uniqueUsers);
summaryContainer.appendChild(usersCard);
summaryDiv.appendChild(summaryContainer);
// Add pie chart for tool usage
if (totalRequests > 0) {
const chartContainer = document.createElement('div');
chartContainer.className = 'chart-container';
const chartWrapper = document.createElement('div');
chartWrapper.className = 'chart-wrapper';
const chartTitle = document.createElement('h3');
chartTitle.className = 'chart-title';
chartTitle.textContent = 'Tool Usage Distribution';
chartWrapper.appendChild(chartTitle);
const canvas = document.createElement('canvas');
canvas.id = 'toolUsageChart';
canvas.style.maxHeight = '300px';
chartWrapper.appendChild(canvas);
chartContainer.appendChild(chartWrapper);
summaryDiv.appendChild(chartContainer);
// Create pie chart
createToolUsagePieChart(toolCounts);
}
}
function createSummaryCard(title, value) {
const card = document.createElement('div');
card.className = 'summary-card';
const heading = document.createElement('h3');
heading.textContent = title;
const valueElement = document.createElement('p');
valueElement.textContent = value;
card.appendChild(heading);
card.appendChild(valueElement);
return card;
}
function createToolUsagePieChart(toolCounts) {
const ctx = document.getElementById('toolUsageChart').getContext('2d');
// Prepare data for the pie chart
const labels = [];
const data = [];
const backgroundColors = [
'#FF6384',
'#36A2EB',
'#FFCE56',
'#4BC0C0',
'#9966FF',
'#FF9F40',
'#FF6384'
];
let colorIndex = 0;
for (const [tool, count] of Object.entries(toolCounts)) {
const toolName = tool === 'TEXT2VOICE' ? 'Text-to-Voice' :
(tool === 'TEXT2IMAGE' || tool === 'TXT2IMAGE') ? 'Text-to-Image' : tool;
labels.push(toolName);
data.push(count);
colorIndex++;
}
new Chart(ctx, {
type: 'pie',
data: {
labels: labels,
datasets: [{
data: data,
backgroundColor: backgroundColors.slice(0, labels.length),
borderWidth: 2,
borderColor: '#fff'
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom',
labels: {
padding: 20,
font: {
family: 'Montserrat',
size: 12
}
}
},
tooltip: {
callbacks: {
label: function(context) {
const total = context.dataset.data.reduce((a, b) => a + b, 0);
const percentage = ((context.parsed / total) * 100).toFixed(1);
return `${context.label}: ${context.parsed} (${percentage}%)`;
}
}
}
}
}
});
}
function processUserPivot(data) {
const userPivotDiv = document.querySelector('#tab-byUser');
userPivotDiv.innerHTML = '';
// Group by user
const userGroups = {};
data.forEach(item => {
const user = item.data.USER || 'Unknown';
const tool = item.data.TOOL || 'Unknown';
if (!userGroups[user]) {
userGroups[user] = {
tools: {},
totalCount: 0
};
}
if (!userGroups[user].tools[tool]) {
userGroups[user].tools[tool] = 0;
}
userGroups[user].tools[tool]++;
userGroups[user].totalCount++;
});
// Create the table
const table = document.createElement('table');
const thead = document.createElement('thead');
const tbody = document.createElement('tbody');
// Create header row
const headerRow = document.createElement('tr');
const headers = ['User', 'Tool Type', 'Count'];
headers.forEach(headerText => {
const header = document.createElement('th');
header.textContent = headerText;
headerRow.appendChild(header);
});
thead.appendChild(headerRow);
table.appendChild(thead);
// Create table body
let grandTotal = 0;
for (const user in userGroups) {
const userData = userGroups[user];
let firstRow = true;
for (const tool in userData.tools) {
const row = document.createElement('tr');
// User column (only show in first row of group)
const userCell = document.createElement('td');
userCell.textContent = firstRow ? user : '';
if (firstRow) {
userCell.style.fontWeight = 'bold';
}
row.appendChild(userCell);
// Tool column
const toolCell = document.createElement('td');
toolCell.textContent = tool;
row.appendChild(toolCell);
// Count column
const countCell = document.createElement('td');
countCell.textContent = userData.tools[tool];
row.appendChild(countCell);
tbody.appendChild(row);
firstRow = false;
}
// Add a total row for this user
const totalRow = document.createElement('tr');
const totalLabelCell = document.createElement('td');
totalLabelCell.textContent = '';
totalRow.appendChild(totalLabelCell);
const totalToolCell = document.createElement('td');
totalToolCell.textContent = 'Total';
totalToolCell.style.fontWeight = 'bold';
totalRow.appendChild(totalToolCell);
const totalCountCell = document.createElement('td');
totalCountCell.textContent = userData.totalCount;
totalCountCell.style.fontWeight = 'bold';
totalRow.appendChild(totalCountCell);
tbody.appendChild(totalRow);
// Add an empty row as a separator
const spacerRow = document.createElement('tr');
const spacerCell = document.createElement('td');
spacerCell.setAttribute('colspan', '3');
spacerCell.style.height = '4px';
spacerRow.appendChild(spacerCell);
tbody.appendChild(spacerRow);
grandTotal += userData.totalCount;
}
// Add grand total row
const grandTotalRow = document.createElement('tr');
const grandTotalLabelCell = document.createElement('td');
grandTotalLabelCell.textContent = 'GRAND TOTAL';
grandTotalLabelCell.style.fontWeight = 'bold';
grandTotalLabelCell.style.fontSize = '14px';
grandTotalRow.appendChild(grandTotalLabelCell);
const grandTotalBlankCell = document.createElement('td');
grandTotalRow.appendChild(grandTotalBlankCell);
const grandTotalCountCell = document.createElement('td');
grandTotalCountCell.textContent = grandTotal;
grandTotalCountCell.style.fontWeight = 'bold';
grandTotalCountCell.style.fontSize = '14px';
grandTotalRow.appendChild(grandTotalCountCell);
tbody.appendChild(grandTotalRow);
table.appendChild(tbody);
userPivotDiv.appendChild(table);
}
function processModelPivot(data) {
const modelPivotDiv = document.querySelector('#tab-byModel');
modelPivotDiv.innerHTML = '';
// Group by model
const modelGroups = {};
data.forEach(item => {
const model = item.data.MODEL || 'Unknown';
const tool = item.data.TOOL || 'Unknown';
if (!modelGroups[model]) {
modelGroups[model] = {
tools: {},
totalCount: 0
};
}
if (!modelGroups[model].tools[tool]) {
modelGroups[model].tools[tool] = 0;
}
modelGroups[model].tools[tool]++;
modelGroups[model].totalCount++;
});
// Create the table
const table = document.createElement('table');
const thead = document.createElement('thead');
const tbody = document.createElement('tbody');
// Create header row
const headerRow = document.createElement('tr');
const headers = ['Model', 'Tool Type', 'Count'];
headers.forEach(headerText => {
const header = document.createElement('th');
header.textContent = headerText;
headerRow.appendChild(header);
});
thead.appendChild(headerRow);
table.appendChild(thead);
// Create table body
let grandTotal = 0;
for (const model in modelGroups) {
const modelData = modelGroups[model];
let firstRow = true;
for (const tool in modelData.tools) {
const row = document.createElement('tr');
// Model column (only show in first row of group)
const modelCell = document.createElement('td');
modelCell.textContent = firstRow ? model : '';
if (firstRow) {
modelCell.style.fontWeight = 'bold';
}
row.appendChild(modelCell);
// Tool column
const toolCell = document.createElement('td');
toolCell.textContent = tool;
row.appendChild(toolCell);
// Count column
const countCell = document.createElement('td');
countCell.textContent = modelData.tools[tool];
row.appendChild(countCell);
tbody.appendChild(row);
firstRow = false;
}
// Add a total row for this model
const totalRow = document.createElement('tr');
const totalLabelCell = document.createElement('td');
totalLabelCell.textContent = '';
totalRow.appendChild(totalLabelCell);
const totalToolCell = document.createElement('td');
totalToolCell.textContent = 'Total';
totalToolCell.style.fontWeight = 'bold';
totalRow.appendChild(totalToolCell);
const totalCountCell = document.createElement('td');
totalCountCell.textContent = modelData.totalCount;
totalCountCell.style.fontWeight = 'bold';
totalRow.appendChild(totalCountCell);
tbody.appendChild(totalRow);
// Add an empty row as a separator
const spacerRow = document.createElement('tr');
const spacerCell = document.createElement('td');
spacerCell.setAttribute('colspan', '3');
spacerCell.style.height = '4px';
spacerRow.appendChild(spacerCell);
tbody.appendChild(spacerRow);
grandTotal += modelData.totalCount;
}
// Add grand total row
const grandTotalRow = document.createElement('tr');
const grandTotalLabelCell = document.createElement('td');
grandTotalLabelCell.textContent = 'GRAND TOTAL';
grandTotalLabelCell.style.fontWeight = 'bold';
grandTotalLabelCell.style.fontSize = '14px';
grandTotalRow.appendChild(grandTotalLabelCell);
const grandTotalBlankCell = document.createElement('td');
grandTotalRow.appendChild(grandTotalBlankCell);
const grandTotalCountCell = document.createElement('td');
grandTotalCountCell.textContent = grandTotal;
grandTotalCountCell.style.fontWeight = 'bold';
grandTotalCountCell.style.fontSize = '14px';
grandTotalRow.appendChild(grandTotalCountCell);
tbody.appendChild(grandTotalRow);
table.appendChild(tbody);
modelPivotDiv.appendChild(table);
}
function processSubToolPivot(data) {
const subToolPivotDiv = document.querySelector('#tab-bySubTool');
subToolPivotDiv.innerHTML = '';
// Group by sub-tool
const subToolGroups = {};
data.forEach(item => {
const subTool = item.data.SUB_TOOL || 'Unknown';
const tool = item.data.TOOL || 'Unknown';
if (!subToolGroups[subTool]) {
subToolGroups[subTool] = {
tools: {},
totalCount: 0
};
}
if (!subToolGroups[subTool].tools[tool]) {
subToolGroups[subTool].tools[tool] = 0;
}
subToolGroups[subTool].tools[tool]++;
subToolGroups[subTool].totalCount++;
});
// Create the table
const table = document.createElement('table');
const thead = document.createElement('thead');
const tbody = document.createElement('tbody');
// Create header row
const headerRow = document.createElement('tr');
const headers = ['Sub-Tool', 'Tool Type', 'Count'];
headers.forEach(headerText => {
const header = document.createElement('th');
header.textContent = headerText;
headerRow.appendChild(header);
});
thead.appendChild(headerRow);
table.appendChild(thead);
// Create table body
let grandTotal = 0;
for (const subTool in subToolGroups) {
const subToolData = subToolGroups[subTool];
let firstRow = true;
for (const tool in subToolData.tools) {
const row = document.createElement('tr');
// Sub-tool column (only show in first row of group)
const subToolCell = document.createElement('td');
subToolCell.textContent = firstRow ? subTool : '';
if (firstRow) {
subToolCell.style.fontWeight = 'bold';
}
row.appendChild(subToolCell);
// Tool column
const toolCell = document.createElement('td');
toolCell.textContent = tool;
row.appendChild(toolCell);
// Count column
const countCell = document.createElement('td');
countCell.textContent = subToolData.tools[tool];
row.appendChild(countCell);
tbody.appendChild(row);
firstRow = false;
}
// Add a total row for this sub-tool
const totalRow = document.createElement('tr');
const totalLabelCell = document.createElement('td');
totalLabelCell.textContent = '';
totalRow.appendChild(totalLabelCell);
const totalToolCell = document.createElement('td');
totalToolCell.textContent = 'Total';
totalToolCell.style.fontWeight = 'bold';
totalRow.appendChild(totalToolCell);
const totalCountCell = document.createElement('td');
totalCountCell.textContent = subToolData.totalCount;
totalCountCell.style.fontWeight = 'bold';
totalRow.appendChild(totalCountCell);
tbody.appendChild(totalRow);
// Add an empty row as a separator
const spacerRow = document.createElement('tr');
const spacerCell = document.createElement('td');
spacerCell.setAttribute('colspan', '3');
spacerCell.style.height = '4px';
spacerRow.appendChild(spacerCell);
tbody.appendChild(spacerRow);
grandTotal += subToolData.totalCount;
}
// Add grand total row
const grandTotalRow = document.createElement('tr');
const grandTotalLabelCell = document.createElement('td');
grandTotalLabelCell.textContent = 'GRAND TOTAL';
grandTotalLabelCell.style.fontWeight = 'bold';
grandTotalLabelCell.style.fontSize = '14px';
grandTotalRow.appendChild(grandTotalLabelCell);
const grandTotalBlankCell = document.createElement('td');
grandTotalRow.appendChild(grandTotalBlankCell);
const grandTotalCountCell = document.createElement('td');
grandTotalCountCell.textContent = grandTotal;
grandTotalCountCell.style.fontWeight = 'bold';
grandTotalCountCell.style.fontSize = '14px';
grandTotalRow.appendChild(grandTotalCountCell);
tbody.appendChild(grandTotalRow);
table.appendChild(tbody);
subToolPivotDiv.appendChild(table);
}
function processDetailsView(data) {
const detailsDiv = document.querySelector('#tab-details');
detailsDiv.innerHTML = '';
// Create the table
const table = document.createElement('table');
const thead = document.createElement('thead');
const tbody = document.createElement('tbody');
// Create header row
const headerRow = document.createElement('tr');
const headers = ['Date', 'Tool', 'User', 'Model', 'Sub-Tool', 'Prompt'];
headers.forEach(headerText => {
const header = document.createElement('th');
header.textContent = headerText;
headerRow.appendChild(header);
});
thead.appendChild(headerRow);
table.appendChild(thead);
// Sort data by date (newest first)
const sortedData = [...data].sort((a, b) => {
const dateA = new Date(a.data.Date || 0);
const dateB = new Date(b.data.Date || 0);
return dateB - dateA;
});
// Create table body
sortedData.forEach(item => {
const row = document.createElement('tr');
// Date column
const dateCell = document.createElement('td');
if (item.data.Date) {
const date = new Date(item.data.Date);
dateCell.textContent = date.toLocaleDateString() + ' ' +
date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
} else {
dateCell.textContent = 'N/A';
}
row.appendChild(dateCell);
// Tool column
const toolCell = document.createElement('td');
toolCell.textContent = item.data.TOOL || 'N/A';
row.appendChild(toolCell);
// User column
const userCell = document.createElement('td');
userCell.textContent = item.data.USER || 'N/A';
row.appendChild(userCell);
// Model column
const modelCell = document.createElement('td');
modelCell.textContent = item.data.MODEL || 'N/A';
row.appendChild(modelCell);
// Sub-tool column
const subToolCell = document.createElement('td');
subToolCell.textContent = item.data.SUB_TOOL || 'N/A';
row.appendChild(subToolCell);
// Prompt column
const promptCell = document.createElement('td');
promptCell.textContent = item.data.PROMPT || 'N/A';
row.appendChild(promptCell);
tbody.appendChild(row);
});
table.appendChild(tbody);
detailsDiv.appendChild(table);
}
});
</script>
</body>
</html>