Ryan Malloy 44ed9936b7 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>
2025-08-11 02:59:21 -06:00

192 lines
7.5 KiB
HTML

{% 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 %}