/**
* Dashboard simplificado sin importaciones complejas
* Para depurar el problema de apiService
*/
console.log('🚀 Starting simplified dashboard');
const { createApp, reactive } = Vue;
// API service inline (sin importaciones)
const apiService = {
baseUrl: `${window.location.protocol}//${window.location.host}/api`,
isConnected: false,
async request(method, endpoint, data = null) {
const url = `${this.baseUrl}${endpoint}`;
const config = {
method,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
};
if (data && (method === 'POST' || method === 'PUT' || method === 'PATCH')) {
config.body = JSON.stringify(data);
}
try {
const response = await fetch(url, config);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error(`❌ Request failed ${method} ${endpoint}:`, error);
throw error;
}
},
async connect() {
try {
const response = await this.request('GET', '/stats');
if (response) {
this.isConnected = true;
console.log('✅ API Service connected');
return true;
}
} catch (error) {
console.error('❌ API connection failed:', error);
return false;
}
},
async getStats() {
return await this.request('GET', '/stats');
},
async getConversationTree(filters = {}) {
const params = new URLSearchParams();
if (filters.project) params.append('project', filters.project);
if (filters.limit) params.append('limit', filters.limit);
if (filters.hours_back) params.append('hours_back', filters.hours_back);
const endpoint = `/conversations/tree${params.toString() ? '?' + params.toString() : ''}`;
return await this.request('GET', endpoint);
}
};
// Store reactivo simple
const store = reactive({
activeView: 'dashboard',
isLoading: false,
error: null,
stats: {
total_messages: 0,
messages_last_24h: 0,
projects: 0,
uptime: 0
},
conversations: []
});
// Dashboard component
const Dashboard = {
setup() {
return {
store,
apiService
};
},
async mounted() {
console.log('📊 Dashboard mounted');
await this.initializeData();
},
methods: {
async initializeData() {
try {
store.isLoading = true;
// Connect to API
await apiService.connect();
// Load initial data
const stats = await apiService.getStats();
store.stats = stats;
const conversations = await apiService.getConversationTree();
store.conversations = conversations.projects || [];
console.log('✅ Data loaded successfully');
store.error = null;
} catch (error) {
console.error('❌ Failed to load data:', error);
store.error = error.message;
} finally {
store.isLoading = false;
}
},
formatNumber(num) {
return new Intl.NumberFormat().format(num || 0);
},
formatUptime(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
return `${hours}h ${minutes}m`;
}
},
template: `
<div class="min-h-screen bg-gray-50">
<!-- Header -->
<header class="bg-white shadow-sm border-b">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center py-6">
<div class="flex items-center">
<h1 class="text-2xl font-bold text-gray-900">
Claude Conversation Logger
</h1>
<div class="ml-4 flex items-center">
<div class="flex items-center text-sm text-gray-500">
<div class="w-2 h-2 bg-green-500 rounded-full mr-2"></div>
Connected
</div>
</div>
</div>
</div>
</div>
</header>
<!-- Main Content -->
<main class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
<!-- Loading State -->
<div v-if="store.isLoading" class="text-center py-12">
<div class="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
<p class="mt-2 text-gray-600">Loading dashboard...</p>
</div>
<!-- Error State -->
<div v-else-if="store.error" class="bg-red-50 border border-red-200 rounded-md p-4">
<div class="flex">
<div class="flex-shrink-0">
<i class="fas fa-exclamation-circle text-red-400"></i>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-red-800">Error</h3>
<div class="mt-2 text-sm text-red-700">
{{ store.error }}
</div>
<div class="mt-4">
<button @click="initializeData" class="bg-red-100 hover:bg-red-200 text-red-800 px-3 py-2 rounded text-sm">
Try Again
</button>
</div>
</div>
</div>
</div>
<!-- Dashboard Content -->
<div v-else class="space-y-6">
<!-- Stats Grid -->
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-4">
<!-- Total Messages -->
<div class="bg-white overflow-hidden shadow rounded-lg">
<div class="p-5">
<div class="flex items-center">
<div class="flex-shrink-0">
<i class="fas fa-comments text-blue-500 text-2xl"></i>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">
Total Messages
</dt>
<dd class="text-lg font-medium text-gray-900">
{{ formatNumber(store.stats.total_messages) }}
</dd>
</dl>
</div>
</div>
</div>
</div>
<!-- Messages Last 24h -->
<div class="bg-white overflow-hidden shadow rounded-lg">
<div class="p-5">
<div class="flex items-center">
<div class="flex-shrink-0">
<i class="fas fa-clock text-green-500 text-2xl"></i>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">
Last 24h
</dt>
<dd class="text-lg font-medium text-gray-900">
{{ formatNumber(store.stats.messages_last_24h) }}
</dd>
</dl>
</div>
</div>
</div>
</div>
<!-- Projects -->
<div class="bg-white overflow-hidden shadow rounded-lg">
<div class="p-5">
<div class="flex items-center">
<div class="flex-shrink-0">
<i class="fas fa-folder text-purple-500 text-2xl"></i>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">
Projects
</dt>
<dd class="text-lg font-medium text-gray-900">
{{ formatNumber(store.stats.projects) }}
</dd>
</dl>
</div>
</div>
</div>
</div>
<!-- Uptime -->
<div class="bg-white overflow-hidden shadow rounded-lg">
<div class="p-5">
<div class="flex items-center">
<div class="flex-shrink-0">
<i class="fas fa-server text-orange-500 text-2xl"></i>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">
Uptime
</dt>
<dd class="text-lg font-medium text-gray-900">
{{ formatUptime(store.stats.uptime) }}
</dd>
</dl>
</div>
</div>
</div>
</div>
</div>
<!-- Conversations -->
<div class="bg-white shadow rounded-lg">
<div class="px-4 py-5 sm:p-6">
<h3 class="text-lg leading-6 font-medium text-gray-900 mb-4">
Recent Conversations
</h3>
<div v-if="store.conversations.length === 0" class="text-center py-8 text-gray-500">
No conversations found
</div>
<div v-else class="space-y-4">
<div v-for="project in store.conversations" :key="project.project_name"
class="border border-gray-200 rounded-lg p-4">
<div class="flex justify-between items-start mb-2">
<h4 class="text-md font-semibold text-gray-900">
{{ project.project_name }}
</h4>
<div class="text-sm text-gray-500">
{{ project.total_sessions }} sessions
</div>
</div>
<div class="text-sm text-gray-600 mb-2">
{{ project.total_messages }} messages
</div>
<div v-if="project.sessions && project.sessions.length > 0" class="space-y-2">
<div v-for="session in project.sessions.slice(0, 3)" :key="session.session_id"
class="bg-gray-50 rounded p-3">
<div class="flex justify-between items-start">
<div>
<div class="text-sm font-medium text-gray-900">
{{ session.session_id.substring(0, 12) }}...
</div>
<div class="text-xs text-gray-500">
{{ session.message_count }} messages
</div>
</div>
<div class="text-xs text-gray-400">
{{ new Date(session.last_message).toLocaleString() }}
</div>
</div>
<div v-if="session.description" class="mt-2 text-xs text-gray-600">
{{ session.description }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
`
};
// Initialize app
const app = createApp({
components: {
Dashboard
},
template: '<Dashboard />'
});
app.mount('#app');
console.log('✅ Simplified dashboard initialized');