- Add ImageUploadService for public URL generation (Meta APIs) - Create PublisherManager for unified multi-platform publishing - Add /api/publish endpoints (single, multiple, thread, test) - Add compose page in dashboard for creating posts - Add connection test script (scripts/test_connections.py) - Update navigation with compose link and logout New endpoints: - POST /api/publish/single - Publish to one platform - POST /api/publish/multiple - Publish to multiple platforms - POST /api/publish/thread - Publish thread (X/Threads) - GET /api/publish/test - Test all API connections - GET /api/publish/platforms - List available platforms Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
183 lines
8.4 KiB
HTML
183 lines
8.4 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Dashboard - Social Media Automation</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
|
<style>
|
|
body { background-color: #1a1a2e; color: #eee; }
|
|
.card { background-color: #16213e; border-radius: 12px; }
|
|
.accent { color: #d4a574; }
|
|
.btn-primary { background-color: #d4a574; color: #1a1a2e; }
|
|
.btn-primary:hover { background-color: #c49564; }
|
|
</style>
|
|
</head>
|
|
<body class="min-h-screen">
|
|
<!-- Header -->
|
|
<header class="bg-gray-900 border-b border-gray-800 px-6 py-4">
|
|
<div class="flex justify-between items-center">
|
|
<h1 class="text-2xl font-bold">
|
|
<span class="accent">Consultoría AS</span> - Social Media
|
|
</h1>
|
|
<nav class="flex gap-4">
|
|
<a href="/dashboard" class="px-4 py-2 rounded bg-gray-800">Home</a>
|
|
<a href="/dashboard/compose" class="px-4 py-2 rounded hover:bg-gray-800 accent">+ Crear Post</a>
|
|
<a href="/dashboard/posts" class="px-4 py-2 rounded hover:bg-gray-800">Posts</a>
|
|
<a href="/dashboard/calendar" class="px-4 py-2 rounded hover:bg-gray-800">Calendario</a>
|
|
<a href="/dashboard/interactions" class="px-4 py-2 rounded hover:bg-gray-800">Interacciones</a>
|
|
<a href="/logout" class="px-4 py-2 rounded hover:bg-gray-800 text-red-400">Salir</a>
|
|
</nav>
|
|
</div>
|
|
</header>
|
|
|
|
<main class="container mx-auto px-6 py-8">
|
|
<!-- Stats -->
|
|
<div class="grid grid-cols-5 gap-4 mb-8">
|
|
<div class="card p-4 text-center">
|
|
<div class="text-3xl font-bold accent">{{ stats.posts_today }}</div>
|
|
<div class="text-gray-400 text-sm">Posts Hoy</div>
|
|
</div>
|
|
<div class="card p-4 text-center">
|
|
<div class="text-3xl font-bold accent">{{ stats.posts_week }}</div>
|
|
<div class="text-gray-400 text-sm">Posts Semana</div>
|
|
</div>
|
|
<div class="card p-4 text-center">
|
|
<div class="text-3xl font-bold text-yellow-500">{{ stats.pending_approval }}</div>
|
|
<div class="text-gray-400 text-sm">Pendientes</div>
|
|
</div>
|
|
<div class="card p-4 text-center">
|
|
<div class="text-3xl font-bold text-blue-500">{{ stats.scheduled }}</div>
|
|
<div class="text-gray-400 text-sm">Programados</div>
|
|
</div>
|
|
<div class="card p-4 text-center">
|
|
<div class="text-3xl font-bold text-red-500">{{ stats.interactions_pending }}</div>
|
|
<div class="text-gray-400 text-sm">Interacciones</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-2 gap-6">
|
|
<!-- Pending Approval -->
|
|
<div class="card p-6">
|
|
<h2 class="text-xl font-semibold mb-4">Pendientes de Aprobación</h2>
|
|
{% if pending_posts %}
|
|
{% for post in pending_posts %}
|
|
<div class="border-b border-gray-700 py-4 last:border-0">
|
|
<div class="flex justify-between items-start mb-2">
|
|
<span class="bg-blue-900 text-blue-300 px-2 py-1 rounded text-xs">
|
|
{{ post.content_type }}
|
|
</span>
|
|
<span class="text-gray-500 text-sm">
|
|
{{ post.scheduled_at }}
|
|
</span>
|
|
</div>
|
|
<p class="text-gray-300 mb-3">{{ post.content[:200] }}...</p>
|
|
<div class="flex gap-2">
|
|
<button onclick="approvePost({{ post.id }})"
|
|
class="btn-primary px-3 py-1 rounded text-sm">
|
|
Aprobar
|
|
</button>
|
|
<button onclick="rejectPost({{ post.id }})"
|
|
class="bg-red-900 text-red-300 px-3 py-1 rounded text-sm">
|
|
Rechazar
|
|
</button>
|
|
<button onclick="editPost({{ post.id }})"
|
|
class="bg-gray-700 px-3 py-1 rounded text-sm">
|
|
Editar
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<p class="text-gray-500">No hay posts pendientes</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Scheduled -->
|
|
<div class="card p-6">
|
|
<h2 class="text-xl font-semibold mb-4">Próximas Publicaciones</h2>
|
|
{% if scheduled_posts %}
|
|
{% for post in scheduled_posts %}
|
|
<div class="border-b border-gray-700 py-3 last:border-0 flex items-center gap-4">
|
|
<div class="text-center">
|
|
<div class="text-accent font-bold">{{ post.scheduled_at }}</div>
|
|
</div>
|
|
<div class="flex-1">
|
|
<span class="text-xs bg-gray-700 px-2 py-1 rounded">
|
|
{{ post.content_type }}
|
|
</span>
|
|
<p class="text-gray-400 text-sm mt-1">
|
|
{{ post.content[:100] }}...
|
|
</p>
|
|
</div>
|
|
<div class="flex gap-1">
|
|
{% for platform in post.platforms %}
|
|
<span class="text-xs bg-gray-800 px-2 py-1 rounded">
|
|
{{ platform }}
|
|
</span>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<p class="text-gray-500">No hay posts programados</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Interactions -->
|
|
<div class="card p-6 mt-6">
|
|
<h2 class="text-xl font-semibold mb-4">Interacciones Recientes</h2>
|
|
{% if recent_interactions %}
|
|
<div class="grid gap-4">
|
|
{% for interaction in recent_interactions %}
|
|
<div class="bg-gray-800 rounded-lg p-4">
|
|
<div class="flex justify-between items-start mb-2">
|
|
<div>
|
|
<span class="font-semibold">@{{ interaction.author_username }}</span>
|
|
<span class="text-gray-500 text-sm ml-2">{{ interaction.platform }}</span>
|
|
</div>
|
|
<span class="text-gray-500 text-xs">{{ interaction.interaction_at }}</span>
|
|
</div>
|
|
<p class="text-gray-300">{{ interaction.content }}</p>
|
|
<div class="mt-3 flex gap-2">
|
|
<button class="bg-accent text-gray-900 px-3 py-1 rounded text-sm">
|
|
Responder
|
|
</button>
|
|
<button class="bg-gray-700 px-3 py-1 rounded text-sm">
|
|
Marcar como Lead
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<p class="text-gray-500">No hay interacciones pendientes</p>
|
|
{% endif %}
|
|
</div>
|
|
</main>
|
|
|
|
<script>
|
|
async function approvePost(postId) {
|
|
const response = await fetch(`/api/posts/${postId}/approve`, { method: 'POST' });
|
|
if (response.ok) {
|
|
location.reload();
|
|
}
|
|
}
|
|
|
|
async function rejectPost(postId) {
|
|
if (confirm('¿Seguro que quieres rechazar este post?')) {
|
|
const response = await fetch(`/api/posts/${postId}/reject`, { method: 'POST' });
|
|
if (response.ok) {
|
|
location.reload();
|
|
}
|
|
}
|
|
}
|
|
|
|
function editPost(postId) {
|
|
window.location.href = `/posts/${postId}/edit`;
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|