## Documentation System - Create complete documentation hub at /dashboard/docs with: - Getting Started guide with quick setup and troubleshooting - Hook Setup Guide with platform-specific configurations - API Reference with all endpoints and examples - FAQ with searchable questions and categories - Add responsive design with interactive features - Update navigation in base template ## Tool Call Tracking - Add ToolCall model for tracking Claude Code tool usage - Create /api/tool-calls endpoints for recording and analytics - Add tool_call hook type with auto-session detection - Include tool calls in project statistics and recalculation - Track tool names, parameters, execution time, and success rates ## Project Enhancements - Add project timeline and statistics pages (fix 404 errors) - Create recalculation script for fixing zero statistics - Update project stats to include tool call counts - Enhance session model with tool call relationships ## Infrastructure - Switch from requirements.txt to pyproject.toml/uv.lock - Add data import functionality for claude.json files - Update database connection to include all new models - Add comprehensive API documentation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
353 lines
13 KiB
HTML
353 lines
13 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}{{ title }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<div class="col-md-12">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h2>Project Statistics</h2>
|
|
<a href="/dashboard/projects" class="btn btn-outline-secondary">← Back to Projects</a>
|
|
</div>
|
|
|
|
<div id="project-info" class="card mb-4">
|
|
<div class="card-body">
|
|
<h5 class="card-title">Loading project information...</h5>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5>Time Period</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<select id="dayRange" class="form-select">
|
|
<option value="7">Last 7 days</option>
|
|
<option value="30" selected>Last 30 days</option>
|
|
<option value="90">Last 90 days</option>
|
|
<option value="365">Last year</option>
|
|
<option value="0">All time</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6 mb-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5>Summary</h5>
|
|
</div>
|
|
<div class="card-body" id="summary-stats">
|
|
Loading...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5>Session Statistics</h5>
|
|
</div>
|
|
<div class="card-body" id="session-stats">
|
|
Loading...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6 mb-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5>Activity Breakdown</h5>
|
|
</div>
|
|
<div class="card-body" id="activity-stats">
|
|
Loading...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5>Tool Usage</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<canvas id="toolsChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6 mb-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5>Language Usage</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<canvas id="languagesChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-12 mb-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5>Productivity Trends</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<canvas id="trendsChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
<script>
|
|
const projectId = {{ project_id }};
|
|
let statsData = {};
|
|
let charts = {};
|
|
|
|
async function loadProjectStats() {
|
|
try {
|
|
const days = document.getElementById('dayRange').value;
|
|
const response = await fetch(`/api/projects/${projectId}/stats?days=${days}`);
|
|
const data = await response.json();
|
|
|
|
if (response.ok) {
|
|
statsData = data;
|
|
displayStats();
|
|
updateCharts();
|
|
} else {
|
|
throw new Error(data.detail || 'Failed to load stats');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading stats:', error);
|
|
document.getElementById('summary-stats').innerHTML = `
|
|
<div class="alert alert-danger">
|
|
Failed to load statistics: ${error.message}
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
async function loadProjectInfo() {
|
|
try {
|
|
const response = await fetch(`/api/projects/${projectId}`);
|
|
const project = await response.json();
|
|
|
|
if (response.ok) {
|
|
document.getElementById('project-info').innerHTML = `
|
|
<div class="card-body">
|
|
<h5 class="card-title">${project.name}</h5>
|
|
<p class="card-text">
|
|
<strong>Path:</strong> ${project.path}<br>
|
|
<strong>Git Repository:</strong> ${project.git_repo || 'Not a git repo'}<br>
|
|
<strong>Languages:</strong> ${project.languages ? project.languages.join(', ') : 'Unknown'}
|
|
</p>
|
|
</div>
|
|
`;
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading project info:', error);
|
|
}
|
|
}
|
|
|
|
function displayStats() {
|
|
// Summary stats
|
|
document.getElementById('summary-stats').innerHTML = `
|
|
<div class="row text-center">
|
|
<div class="col-md-3">
|
|
<h4 class="text-primary">${statsData.session_statistics.total_sessions}</h4>
|
|
<small>Sessions</small>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<h4 class="text-success">${Math.round(statsData.session_statistics.total_time_minutes / 60 * 10) / 10}h</h4>
|
|
<small>Total Time</small>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<h4 class="text-warning">${statsData.activity_statistics.total}</h4>
|
|
<small>Activities</small>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<h4 class="text-info">${statsData.conversation_statistics.total}</h4>
|
|
<small>Conversations</small>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// Session stats
|
|
document.getElementById('session-stats').innerHTML = `
|
|
<p><strong>Total Sessions:</strong> ${statsData.session_statistics.total_sessions}</p>
|
|
<p><strong>Total Time:</strong> ${Math.round(statsData.session_statistics.total_time_minutes / 60 * 10) / 10} hours</p>
|
|
<p><strong>Average Session:</strong> ${statsData.session_statistics.average_session_length_minutes} minutes</p>
|
|
<p><strong>Daily Average Time:</strong> ${statsData.summary.daily_average_time} minutes</p>
|
|
<p><strong>Daily Average Sessions:</strong> ${statsData.summary.daily_average_sessions}</p>
|
|
`;
|
|
|
|
// Activity stats
|
|
const topTools = Object.entries(statsData.activity_statistics.by_tool)
|
|
.sort((a, b) => b[1] - a[1])
|
|
.slice(0, 5)
|
|
.map(([tool, count]) => `<li>${tool}: ${count}</li>`)
|
|
.join('');
|
|
|
|
const topLangs = Object.entries(statsData.activity_statistics.by_language)
|
|
.sort((a, b) => b[1] - a[1])
|
|
.slice(0, 5)
|
|
.map(([lang, count]) => `<li>${lang}: ${count}</li>`)
|
|
.join('');
|
|
|
|
document.getElementById('activity-stats').innerHTML = `
|
|
<p><strong>Total Activities:</strong> ${statsData.activity_statistics.total}</p>
|
|
<p><strong>Most Used Tool:</strong> ${statsData.summary.most_used_tool || 'None'}</p>
|
|
<p><strong>Primary Language:</strong> ${statsData.summary.primary_language || 'None'}</p>
|
|
<div class="row mt-3">
|
|
<div class="col-md-6">
|
|
<strong>Top Tools:</strong>
|
|
<ul class="small">${topTools || '<li>No data</li>'}</ul>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<strong>Languages:</strong>
|
|
<ul class="small">${topLangs || '<li>No data</li>'}</ul>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
function updateCharts() {
|
|
// Destroy existing charts
|
|
Object.values(charts).forEach(chart => chart.destroy());
|
|
charts = {};
|
|
|
|
// Tools chart
|
|
const toolData = Object.entries(statsData.activity_statistics.by_tool)
|
|
.sort((a, b) => b[1] - a[1])
|
|
.slice(0, 10);
|
|
|
|
if (toolData.length > 0) {
|
|
charts.tools = new Chart(document.getElementById('toolsChart'), {
|
|
type: 'doughnut',
|
|
data: {
|
|
labels: toolData.map(([tool]) => tool),
|
|
datasets: [{
|
|
data: toolData.map(([, count]) => count),
|
|
backgroundColor: [
|
|
'#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF',
|
|
'#FF9F40', '#C9CBCF', '#4BC0C0', '#FF6384', '#36A2EB'
|
|
]
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: true
|
|
}
|
|
});
|
|
}
|
|
|
|
// Languages chart
|
|
const langData = Object.entries(statsData.activity_statistics.by_language)
|
|
.sort((a, b) => b[1] - a[1]);
|
|
|
|
if (langData.length > 0) {
|
|
charts.languages = new Chart(document.getElementById('languagesChart'), {
|
|
type: 'bar',
|
|
data: {
|
|
labels: langData.map(([lang]) => lang),
|
|
datasets: [{
|
|
label: 'Activities',
|
|
data: langData.map(([, count]) => count),
|
|
backgroundColor: '#36A2EB'
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: true,
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Trends chart
|
|
if (statsData.productivity_trends && statsData.productivity_trends.length > 0) {
|
|
charts.trends = new Chart(document.getElementById('trendsChart'), {
|
|
type: 'line',
|
|
data: {
|
|
labels: statsData.productivity_trends.map(day => day.date),
|
|
datasets: [
|
|
{
|
|
label: 'Sessions',
|
|
data: statsData.productivity_trends.map(day => day.sessions),
|
|
borderColor: '#FF6384',
|
|
backgroundColor: 'rgba(255, 99, 132, 0.1)',
|
|
yAxisID: 'y'
|
|
},
|
|
{
|
|
label: 'Time (minutes)',
|
|
data: statsData.productivity_trends.map(day => day.time_minutes),
|
|
borderColor: '#36A2EB',
|
|
backgroundColor: 'rgba(54, 162, 235, 0.1)',
|
|
yAxisID: 'y1'
|
|
}
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: true,
|
|
interaction: {
|
|
mode: 'index',
|
|
intersect: false,
|
|
},
|
|
scales: {
|
|
x: {
|
|
display: true,
|
|
title: {
|
|
display: true,
|
|
text: 'Date'
|
|
}
|
|
},
|
|
y: {
|
|
type: 'linear',
|
|
display: true,
|
|
position: 'left',
|
|
title: {
|
|
display: true,
|
|
text: 'Sessions'
|
|
}
|
|
},
|
|
y1: {
|
|
type: 'linear',
|
|
display: true,
|
|
position: 'right',
|
|
title: {
|
|
display: true,
|
|
text: 'Time (minutes)'
|
|
},
|
|
grid: {
|
|
drawOnChartArea: false,
|
|
},
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Event listeners
|
|
document.getElementById('dayRange').addEventListener('change', loadProjectStats);
|
|
|
|
// Load initial data
|
|
loadProjectInfo();
|
|
loadProjectStats();
|
|
</script>
|
|
{% endblock %} |