## 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>
259 lines
8.1 KiB
JavaScript
259 lines
8.1 KiB
JavaScript
/**
|
|
* API Client for Claude Code Project Tracker
|
|
*/
|
|
|
|
class ApiClient {
|
|
constructor(baseUrl = '') {
|
|
this.baseUrl = baseUrl;
|
|
}
|
|
|
|
async request(endpoint, options = {}) {
|
|
const url = `${this.baseUrl}/api${endpoint}`;
|
|
const config = {
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...options.headers
|
|
},
|
|
...options
|
|
};
|
|
|
|
try {
|
|
const response = await fetch(url, config);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
return await response.json();
|
|
} catch (error) {
|
|
console.error(`API request failed: ${endpoint}`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// Projects API
|
|
async getProjects(limit = 50, offset = 0) {
|
|
return this.request(`/projects?limit=${limit}&offset=${offset}`);
|
|
}
|
|
|
|
async getProject(projectId) {
|
|
return this.request(`/projects/${projectId}`);
|
|
}
|
|
|
|
async getProjectTimeline(projectId, startDate = null, endDate = null) {
|
|
let url = `/projects/${projectId}/timeline`;
|
|
const params = new URLSearchParams();
|
|
|
|
if (startDate) params.append('start_date', startDate);
|
|
if (endDate) params.append('end_date', endDate);
|
|
|
|
if (params.toString()) {
|
|
url += `?${params.toString()}`;
|
|
}
|
|
|
|
return this.request(url);
|
|
}
|
|
|
|
async getProjectStats(projectId, days = 30) {
|
|
return this.request(`/projects/${projectId}/stats?days=${days}`);
|
|
}
|
|
|
|
// Analytics API
|
|
async getProductivityMetrics(projectId = null, days = 30) {
|
|
let url = `/analytics/productivity?days=${days}`;
|
|
if (projectId) {
|
|
url += `&project_id=${projectId}`;
|
|
}
|
|
return this.request(url);
|
|
}
|
|
|
|
async getDevelopmentPatterns(projectId = null, days = 30) {
|
|
let url = `/analytics/patterns?days=${days}`;
|
|
if (projectId) {
|
|
url += `&project_id=${projectId}`;
|
|
}
|
|
return this.request(url);
|
|
}
|
|
|
|
async getLearningInsights(projectId = null, days = 30) {
|
|
let url = `/analytics/learning?days=${days}`;
|
|
if (projectId) {
|
|
url += `&project_id=${projectId}`;
|
|
}
|
|
return this.request(url);
|
|
}
|
|
|
|
async getAnalyticsSummary(projectId = null) {
|
|
let url = '/analytics/summary';
|
|
if (projectId) {
|
|
url += `?project_id=${projectId}`;
|
|
}
|
|
return this.request(url);
|
|
}
|
|
|
|
// Conversations API
|
|
async getConversations(projectId = null, limit = 50, offset = 0) {
|
|
let url = `/conversations?limit=${limit}&offset=${offset}`;
|
|
if (projectId) {
|
|
url += `&project_id=${projectId}`;
|
|
}
|
|
return this.request(url);
|
|
}
|
|
|
|
async searchConversations(query, projectId = null, limit = 20) {
|
|
let url = `/conversations/search?query=${encodeURIComponent(query)}&limit=${limit}`;
|
|
if (projectId) {
|
|
url += `&project_id=${projectId}`;
|
|
}
|
|
return this.request(url);
|
|
}
|
|
|
|
async getConversation(conversationId) {
|
|
return this.request(`/conversations/${conversationId}`);
|
|
}
|
|
|
|
// Activities API
|
|
async getActivities(sessionId = null, toolName = null, limit = 50, offset = 0) {
|
|
let url = `/activities?limit=${limit}&offset=${offset}`;
|
|
if (sessionId) url += `&session_id=${sessionId}`;
|
|
if (toolName) url += `&tool_name=${toolName}`;
|
|
return this.request(url);
|
|
}
|
|
|
|
async getToolUsageStats(sessionId = null, projectId = null, days = 30) {
|
|
let url = `/activities/stats/tools?days=${days}`;
|
|
if (sessionId) url += `&session_id=${sessionId}`;
|
|
if (projectId) url += `&project_id=${projectId}`;
|
|
return this.request(url);
|
|
}
|
|
|
|
async getLanguageUsageStats(projectId = null, days = 30) {
|
|
let url = `/activities/stats/languages?days=${days}`;
|
|
if (projectId) url += `&project_id=${projectId}`;
|
|
return this.request(url);
|
|
}
|
|
|
|
// Waiting Periods API
|
|
async getWaitingPeriods(sessionId = null, projectId = null, limit = 50, offset = 0) {
|
|
let url = `/waiting/periods?limit=${limit}&offset=${offset}`;
|
|
if (sessionId) url += `&session_id=${sessionId}`;
|
|
if (projectId) url += `&project_id=${projectId}`;
|
|
return this.request(url);
|
|
}
|
|
|
|
async getEngagementStats(sessionId = null, projectId = null, days = 7) {
|
|
let url = `/waiting/stats/engagement?days=${days}`;
|
|
if (sessionId) url += `&session_id=${sessionId}`;
|
|
if (projectId) url += `&project_id=${projectId}`;
|
|
return this.request(url);
|
|
}
|
|
|
|
// Git Operations API
|
|
async getGitOperations(sessionId = null, projectId = null, operation = null, limit = 50, offset = 0) {
|
|
let url = `/git/operations?limit=${limit}&offset=${offset}`;
|
|
if (sessionId) url += `&session_id=${sessionId}`;
|
|
if (projectId) url += `&project_id=${projectId}`;
|
|
if (operation) url += `&operation=${operation}`;
|
|
return this.request(url);
|
|
}
|
|
|
|
async getCommitStats(projectId = null, days = 30) {
|
|
let url = `/git/stats/commits?days=${days}`;
|
|
if (projectId) url += `&project_id=${projectId}`;
|
|
return this.request(url);
|
|
}
|
|
|
|
async getGitActivityStats(projectId = null, days = 30) {
|
|
let url = `/git/stats/activity?days=${days}`;
|
|
if (projectId) url += `&project_id=${projectId}`;
|
|
return this.request(url);
|
|
}
|
|
|
|
// Sessions API
|
|
async getSession(sessionId) {
|
|
return this.request(`/sessions/${sessionId}`);
|
|
}
|
|
|
|
// Health check
|
|
async healthCheck() {
|
|
return this.request('/../health');
|
|
}
|
|
}
|
|
|
|
// Create global API client instance
|
|
const apiClient = new ApiClient();
|
|
|
|
// Utility functions for common operations
|
|
const ApiUtils = {
|
|
formatDuration: (minutes) => {
|
|
if (minutes < 60) {
|
|
return `${minutes}m`;
|
|
}
|
|
const hours = Math.floor(minutes / 60);
|
|
const remainingMinutes = minutes % 60;
|
|
return remainingMinutes > 0 ? `${hours}h ${remainingMinutes}m` : `${hours}h`;
|
|
},
|
|
|
|
formatDate: (dateString) => {
|
|
const date = new Date(dateString);
|
|
return date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], {
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
});
|
|
},
|
|
|
|
formatRelativeTime: (dateString) => {
|
|
const date = new Date(dateString);
|
|
const now = new Date();
|
|
const diffMs = now - date;
|
|
const diffMinutes = Math.floor(diffMs / (1000 * 60));
|
|
const diffHours = Math.floor(diffMinutes / 60);
|
|
const diffDays = Math.floor(diffHours / 24);
|
|
|
|
if (diffMinutes < 1) return 'just now';
|
|
if (diffMinutes < 60) return `${diffMinutes}m ago`;
|
|
if (diffHours < 24) return `${diffHours}h ago`;
|
|
if (diffDays < 7) return `${diffDays}d ago`;
|
|
|
|
return date.toLocaleDateString();
|
|
},
|
|
|
|
getToolIcon: (toolName) => {
|
|
const icons = {
|
|
'Edit': 'fas fa-edit',
|
|
'Write': 'fas fa-file-alt',
|
|
'Read': 'fas fa-eye',
|
|
'Bash': 'fas fa-terminal',
|
|
'Grep': 'fas fa-search',
|
|
'Glob': 'fas fa-folder-open',
|
|
'Task': 'fas fa-cogs',
|
|
'WebFetch': 'fas fa-globe'
|
|
};
|
|
return icons[toolName] || 'fas fa-tool';
|
|
},
|
|
|
|
getToolColor: (toolName) => {
|
|
const colors = {
|
|
'Edit': 'success',
|
|
'Write': 'primary',
|
|
'Read': 'info',
|
|
'Bash': 'secondary',
|
|
'Grep': 'warning',
|
|
'Glob': 'info',
|
|
'Task': 'dark',
|
|
'WebFetch': 'primary'
|
|
};
|
|
return colors[toolName] || 'secondary';
|
|
},
|
|
|
|
getEngagementBadge: (score) => {
|
|
if (score >= 0.8) return { class: 'success', text: 'High' };
|
|
if (score >= 0.5) return { class: 'warning', text: 'Medium' };
|
|
return { class: 'danger', text: 'Low' };
|
|
},
|
|
|
|
truncateText: (text, maxLength = 100) => {
|
|
if (!text || text.length <= maxLength) return text;
|
|
return text.substring(0, maxLength) + '...';
|
|
}
|
|
}; |