Files
social-media-automation/dashboard/templates/index.html
Consultoría AS f4f0a2d230 feat: Add recent published section and filter self-interactions
- Add "Publicaciones Recientes" section to dashboard showing published posts
- Filter out self-generated interactions in X API (thread auto-replies)
- Improve username resolution for X interactions using user expansions
- Pass recent_published data to dashboard template

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 23:29:02 +00:00

291 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}Dashboard{% endblock %}
{% block content %}
<div class="animate-fade-in">
<!-- Header -->
<div class="flex items-center justify-between mb-8">
<div>
<h1 class="text-3xl font-bold">Dashboard</h1>
<p class="text-gray-400 mt-1">Bienvenido, {{ user.username }}</p>
</div>
<a href="/compose" class="btn-primary px-6 py-3 rounded-xl font-medium flex items-center gap-2 transition-all">
<span>✍️</span>
<span>Crear Post</span>
</a>
</div>
<!-- Stats Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<div class="stat-card card rounded-2xl p-6">
<div class="flex items-center justify-between mb-4">
<div class="w-12 h-12 rounded-xl bg-blue-500/20 flex items-center justify-center">
<span class="text-2xl">📝</span>
</div>
<span class="text-xs text-gray-500 bg-dark-700 px-2 py-1 rounded-full">Hoy</span>
</div>
<p class="text-3xl font-bold">{{ stats.posts_today }}</p>
<p class="text-gray-400 text-sm mt-1">Posts publicados</p>
</div>
<div class="stat-card card rounded-2xl p-6">
<div class="flex items-center justify-between mb-4">
<div class="w-12 h-12 rounded-xl bg-purple-500/20 flex items-center justify-center">
<span class="text-2xl">📅</span>
</div>
<span class="text-xs text-gray-500 bg-dark-700 px-2 py-1 rounded-full">Semana</span>
</div>
<p class="text-3xl font-bold">{{ stats.posts_week }}</p>
<p class="text-gray-400 text-sm mt-1">Posts esta semana</p>
</div>
<div class="stat-card card rounded-2xl p-6">
<div class="flex items-center justify-between mb-4">
<div class="w-12 h-12 rounded-xl bg-yellow-500/20 flex items-center justify-center">
<span class="text-2xl"></span>
</div>
<span class="text-xs text-yellow-400 bg-yellow-500/20 px-2 py-1 rounded-full">Pendiente</span>
</div>
<p class="text-3xl font-bold">{{ stats.pending_approval }}</p>
<p class="text-gray-400 text-sm mt-1">Por aprobar</p>
</div>
<div class="stat-card card rounded-2xl p-6">
<div class="flex items-center justify-between mb-4">
<div class="w-12 h-12 rounded-xl bg-green-500/20 flex items-center justify-center">
<span class="text-2xl">🗓️</span>
</div>
<span class="text-xs text-green-400 bg-green-500/20 px-2 py-1 rounded-full">Programado</span>
</div>
<p class="text-3xl font-bold">{{ stats.scheduled }}</p>
<p class="text-gray-400 text-sm mt-1">Posts programados</p>
</div>
</div>
<!-- Main Content Grid -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Pending Posts -->
<div class="card rounded-2xl p-6">
<div class="flex items-center justify-between mb-6">
<h2 class="text-lg font-semibold flex items-center gap-2">
<span></span>
<span>Pendientes de Aprobación</span>
</h2>
<a href="/posts?status=pending" class="text-primary text-sm hover:underline">Ver todos</a>
</div>
<div class="space-y-4">
{% if pending_posts %}
{% for post in pending_posts %}
<div class="bg-dark-800/50 rounded-xl p-4 hover:bg-dark-700/50 transition-colors">
<p class="text-sm text-gray-300 line-clamp-2">{{ post.content[:100] }}{% if post.content|length > 100 %}...{% endif %}</p>
<div class="flex items-center justify-between mt-3">
<div class="flex items-center gap-2">
{% for platform in post.platforms %}
<span class="text-xs bg-dark-700 px-2 py-1 rounded-full">{{ platform }}</span>
{% endfor %}
</div>
<span class="text-xs text-gray-500">{{ post.created_at[:10] if post.created_at else '-' }}</span>
</div>
</div>
{% endfor %}
{% else %}
<div class="text-center py-8 text-gray-500">
<span class="text-4xl mb-2 block"></span>
<p>No hay posts pendientes</p>
</div>
{% endif %}
</div>
</div>
<!-- Scheduled Posts -->
<div class="card rounded-2xl p-6">
<div class="flex items-center justify-between mb-6">
<h2 class="text-lg font-semibold flex items-center gap-2">
<span>🗓️</span>
<span>Próximas Publicaciones</span>
</h2>
<a href="/calendar" class="text-primary text-sm hover:underline">Ver calendario</a>
</div>
<div class="space-y-4">
{% if scheduled_posts %}
{% for post in scheduled_posts %}
<div class="bg-dark-800/50 rounded-xl p-4 hover:bg-dark-700/50 transition-colors">
<p class="text-sm text-gray-300 line-clamp-2">{{ post.content[:100] }}{% if post.content|length > 100 %}...{% endif %}</p>
<div class="flex items-center justify-between mt-3">
<div class="flex items-center gap-2">
{% for platform in post.platforms %}
<span class="text-xs bg-dark-700 px-2 py-1 rounded-full">{{ platform }}</span>
{% endfor %}
</div>
<span class="text-xs text-green-400">{{ post.scheduled_at[:16] if post.scheduled_at else '-' }}</span>
</div>
</div>
{% endfor %}
{% else %}
<div class="text-center py-8 text-gray-500">
<span class="text-4xl mb-2 block">📭</span>
<p>No hay posts programados</p>
</div>
{% endif %}
</div>
</div>
<!-- Recent Interactions -->
<div class="card rounded-2xl p-6">
<div class="flex items-center justify-between mb-6">
<h2 class="text-lg font-semibold flex items-center gap-2">
<span>💬</span>
<span>Interacciones Recientes</span>
</h2>
<a href="/interactions" class="text-primary text-sm hover:underline">Ver todas</a>
</div>
<div class="space-y-4">
{% if recent_interactions %}
{% for interaction in recent_interactions %}
<div class="bg-dark-800/50 rounded-xl p-4 hover:bg-dark-700/50 transition-colors">
<div class="flex items-center gap-3 mb-2">
<div class="w-8 h-8 rounded-full bg-primary/20 flex items-center justify-center text-sm">
{{ interaction.author_username[0]|upper if interaction.author_username else '?' }}
</div>
<div>
<p class="text-sm font-medium">@{{ interaction.author_username or 'Usuario' }}</p>
<p class="text-xs text-gray-500">{{ interaction.interaction_type }}</p>
</div>
</div>
<p class="text-sm text-gray-400 line-clamp-2">{{ interaction.content[:80] }}{% if interaction.content|length > 80 %}...{% endif %}</p>
</div>
{% endfor %}
{% else %}
<div class="text-center py-8 text-gray-500">
<span class="text-4xl mb-2 block">💤</span>
<p>No hay interacciones pendientes</p>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Recent Published Posts -->
<div class="mt-8">
<div class="card rounded-2xl p-6">
<div class="flex items-center justify-between mb-6">
<h2 class="text-lg font-semibold flex items-center gap-2">
<span></span>
<span>Publicaciones Recientes</span>
</h2>
<a href="/posts?status=published" class="text-primary text-sm hover:underline">Ver todas</a>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5 gap-4">
{% if recent_published %}
{% for post in recent_published %}
<div class="bg-dark-800/50 rounded-xl p-4 hover:bg-dark-700/50 transition-colors">
<div class="flex items-center gap-2 mb-2">
{% for platform in post.platforms %}
<span class="text-xs bg-green-500/20 text-green-400 px-2 py-1 rounded-full">{{ platform }}</span>
{% endfor %}
</div>
<p class="text-sm text-gray-300 line-clamp-3">{{ post.content[:120] }}{% if post.content|length > 120 %}...{% endif %}</p>
<div class="flex items-center justify-between mt-3">
<span class="text-xs text-gray-500">{{ post.published_at[:10] if post.published_at else '-' }}</span>
{% if post.platform_post_ids %}
<a href="https://x.com/i/status/{{ post.platform_post_ids.x }}" target="_blank" class="text-xs text-primary hover:underline" {% if not post.platform_post_ids.x %}style="display:none"{% endif %}>
Ver en X
</a>
{% endif %}
</div>
</div>
{% endfor %}
{% else %}
<div class="col-span-full text-center py-8 text-gray-500">
<span class="text-4xl mb-2 block">📭</span>
<p>No hay publicaciones recientes</p>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="mt-8">
<h2 class="text-lg font-semibold mb-4">Acciones Rápidas</h2>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<a href="/compose" class="card rounded-xl p-4 text-center hover:bg-dark-700/50 transition-all group">
<span class="text-3xl mb-2 block group-hover:scale-110 transition-transform">✍️</span>
<span class="text-sm text-gray-300">Nuevo Post</span>
</a>
<a href="/compose?type=tip" class="card rounded-xl p-4 text-center hover:bg-dark-700/50 transition-all group">
<span class="text-3xl mb-2 block group-hover:scale-110 transition-transform">💡</span>
<span class="text-sm text-gray-300">Generar Tip</span>
</a>
<a href="/analytics" class="card rounded-xl p-4 text-center hover:bg-dark-700/50 transition-all group">
<span class="text-3xl mb-2 block group-hover:scale-110 transition-transform">📊</span>
<span class="text-sm text-gray-300">Ver Analytics</span>
</a>
<a href="/settings" class="card rounded-xl p-4 text-center hover:bg-dark-700/50 transition-all group">
<span class="text-3xl mb-2 block group-hover:scale-110 transition-transform">⚙️</span>
<span class="text-sm text-gray-300">Configuración</span>
</a>
</div>
</div>
<!-- Platform Status -->
<div class="mt-8">
<h2 class="text-lg font-semibold mb-4">Estado de Plataformas</h2>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4" id="platform-status">
<!-- Loaded via JS -->
</div>
</div>
</div>
{% endblock %}
{% block extra_scripts %}
<script>
async function loadPlatformStatus() {
const container = document.getElementById('platform-status');
const platforms = ['x', 'facebook', 'instagram', 'threads'];
container.innerHTML = platforms.map(p => `
<div class="card rounded-xl p-4">
<div class="flex items-center justify-between">
<span class="text-xl">${getPlatformIcon(p)}</span>
<span class="text-xs bg-dark-700 px-2 py-1 rounded-full">Verificando...</span>
</div>
<p class="text-sm mt-2 capitalize">${p}</p>
</div>
`).join('');
try {
const response = await fetch('/api/publish/test');
const data = await response.json();
container.innerHTML = '';
for (const [platform, status] of Object.entries(data)) {
const isConnected = status.configured && status.connected;
container.innerHTML += `
<div class="card rounded-xl p-4">
<div class="flex items-center justify-between">
<span class="text-xl">${getPlatformIcon(platform)}</span>
<span class="text-xs ${isConnected ? 'bg-green-500/20 text-green-400' : 'bg-red-500/20 text-red-400'} px-2 py-1 rounded-full">
${isConnected ? 'Conectado' : 'Desconectado'}
</span>
</div>
<p class="text-sm mt-2 capitalize">${platform}</p>
${status.details?.username ? `<p class="text-xs text-gray-500">@${status.details.username}</p>` : ''}
</div>
`;
}
} catch (error) {
container.innerHTML = '<div class="col-span-4 text-center text-gray-500 py-4">Error cargando estado</div>';
}
}
function getPlatformIcon(platform) {
const icons = { x: '𝕏', facebook: '📘', instagram: '📸', threads: '🧵' };
return icons[platform] || '📱';
}
loadPlatformStatus();
</script>
{% endblock %}