- 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>
1345 lines
No EOL
56 KiB
HTML
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> |