Initial commit: Claude Code Project Tracker
Add comprehensive development intelligence system that tracks: - Development sessions with automatic start/stop - Full conversation history with semantic search - Tool usage and file operation analytics - Think time and engagement analysis - Git activity correlation - Learning pattern recognition - Productivity insights and metrics Features: - FastAPI backend with SQLite database - Modern web dashboard with interactive charts - Claude Code hook integration for automatic tracking - Comprehensive test suite with 100+ tests - Complete API documentation (OpenAPI/Swagger) - Privacy-first design with local data storage 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
commit
44ed9936b7
48 changed files with 9707 additions and 0 deletions
192
app/dashboard/templates/projects.html
Normal file
192
app/dashboard/templates/projects.html
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Projects - Claude Code Project Tracker{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1>
|
||||
<i class="fas fa-folder-open me-2"></i>
|
||||
Projects Overview
|
||||
</h1>
|
||||
<div class="d-flex gap-2">
|
||||
<div class="input-group" style="width: 300px;">
|
||||
<input type="text" class="form-control search-box" placeholder="Search projects..." id="search-input">
|
||||
<button class="btn btn-outline-secondary" type="button">
|
||||
<i class="fas fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-primary" onclick="refreshProjects()">
|
||||
<i class="fas fa-sync-alt me-1"></i>
|
||||
Refresh
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div id="projects-list">
|
||||
<div class="text-center py-4">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<p class="mt-2 text-muted">Loading projects...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
loadProjects();
|
||||
|
||||
// Search functionality
|
||||
document.getElementById('search-input').addEventListener('input', function(e) {
|
||||
filterProjects(e.target.value);
|
||||
});
|
||||
});
|
||||
|
||||
async function loadProjects() {
|
||||
try {
|
||||
const projects = await apiClient.getProjects();
|
||||
displayProjects(projects);
|
||||
} catch (error) {
|
||||
console.error('Failed to load projects:', error);
|
||||
document.getElementById('projects-list').innerHTML = `
|
||||
<div class="text-center text-danger py-4">
|
||||
<i class="fas fa-exclamation-triangle fa-2x mb-2"></i>
|
||||
<p>Failed to load projects</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
function displayProjects(projects) {
|
||||
const container = document.getElementById('projects-list');
|
||||
|
||||
if (!projects.length) {
|
||||
container.innerHTML = `
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="fas fa-folder-plus fa-3x mb-3"></i>
|
||||
<h5>No projects found</h5>
|
||||
<p>Start using Claude Code in a project directory to begin tracking.</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
const projectsHtml = projects.map(project => `
|
||||
<div class="project-card card mb-3 fade-in" data-name="${project.name.toLowerCase()}" data-path="${project.path.toLowerCase()}">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<h5 class="card-title mb-2">
|
||||
<i class="fas fa-folder me-2 text-primary"></i>
|
||||
${project.name}
|
||||
</h5>
|
||||
<p class="text-muted mb-2">
|
||||
<i class="fas fa-map-marker-alt me-1"></i>
|
||||
<code>${project.path}</code>
|
||||
</p>
|
||||
${project.git_repo ? `
|
||||
<p class="text-muted mb-2">
|
||||
<i class="fab fa-git-alt me-1"></i>
|
||||
<a href="${project.git_repo}" target="_blank" class="text-decoration-none">${project.git_repo}</a>
|
||||
</p>
|
||||
` : ''}
|
||||
${project.languages && project.languages.length ? `
|
||||
<div class="mb-2">
|
||||
${project.languages.map(lang => `
|
||||
<span class="badge bg-secondary me-1">${lang}</span>
|
||||
`).join('')}
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
<div class="col-md-4 text-end">
|
||||
<div class="row g-2 text-center">
|
||||
<div class="col-6">
|
||||
<div class="fw-bold text-primary">${project.total_sessions}</div>
|
||||
<small class="text-muted">Sessions</small>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="fw-bold text-success">${ApiUtils.formatDuration(project.total_time_minutes)}</div>
|
||||
<small class="text-muted">Time</small>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="fw-bold text-info">${project.files_modified_count}</div>
|
||||
<small class="text-muted">Files</small>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="fw-bold text-warning">${project.lines_changed_count.toLocaleString()}</div>
|
||||
<small class="text-muted">Lines</small>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-2">
|
||||
<small class="text-muted">
|
||||
Last active: ${ApiUtils.formatRelativeTime(project.last_activity)}
|
||||
</small>
|
||||
<div class="mt-2">
|
||||
<button class="btn btn-outline-primary btn-sm me-1" onclick="viewProjectTimeline(${project.id})">
|
||||
<i class="fas fa-timeline me-1"></i>
|
||||
Timeline
|
||||
</button>
|
||||
<button class="btn btn-outline-secondary btn-sm" onclick="viewProjectStats(${project.id})">
|
||||
<i class="fas fa-chart-bar me-1"></i>
|
||||
Stats
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
container.innerHTML = projectsHtml;
|
||||
}
|
||||
|
||||
function filterProjects(searchTerm) {
|
||||
const projects = document.querySelectorAll('.project-card');
|
||||
const term = searchTerm.toLowerCase();
|
||||
|
||||
projects.forEach(project => {
|
||||
const name = project.dataset.name;
|
||||
const path = project.dataset.path;
|
||||
const matches = name.includes(term) || path.includes(term);
|
||||
|
||||
project.style.display = matches ? 'block' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
function refreshProjects() {
|
||||
const refreshBtn = document.querySelector('[onclick="refreshProjects()"]');
|
||||
const icon = refreshBtn.querySelector('i');
|
||||
|
||||
icon.classList.add('fa-spin');
|
||||
refreshBtn.disabled = true;
|
||||
|
||||
loadProjects().finally(() => {
|
||||
icon.classList.remove('fa-spin');
|
||||
refreshBtn.disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
function viewProjectTimeline(projectId) {
|
||||
// Could open a modal or navigate to a detailed timeline view
|
||||
window.open(`/dashboard/projects/${projectId}/timeline`, '_blank');
|
||||
}
|
||||
|
||||
function viewProjectStats(projectId) {
|
||||
// Could open a modal or navigate to detailed stats
|
||||
window.open(`/dashboard/projects/${projectId}/stats`, '_blank');
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue