Files
urllists/static/templates/dashboard_old.html
T

284 lines
11 KiB
HTML
Raw Normal View History

2025-11-30 12:19:31 +00:00
{% extends "base.html" %}
{% block title %}Dashboard - {{ app_name }}{% endblock %}
{% block content %}
<div class="space-y-4">
<!-- Header -->
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
<div>
<h1 class="text-3xl font-bold">URL Lists</h1>
</div>
<a href="{{ url_for('create_list') }}" class="bg-sky-600 hover:bg-sky-700 text-white font-semibold py-2 px-4 rounded transition duration-200 text-center text-sm whitespace-nowrap">
+ New List
</a>
</div>
<!-- Search and Filter Section -->
<div class="space-y-2">
<!-- My Lists Search -->
<div class="relative">
<input
type="text"
id="searchInput"
placeholder="Search your lists..."
class="w-full bg-slate-900 border border-slate-700 rounded px-4 py-2 text-slate-100 placeholder-slate-500 focus:outline-none focus:border-sky-500 focus:ring-1 focus:ring-sky-500 transition text-sm"
>
</div>
<!-- Search Public Lists -->
<div class="relative">
<input
type="text"
id="publicSearchInput"
placeholder="Search public lists by domain (e.g., github.com)..."
class="w-full bg-slate-900 border border-slate-700 rounded px-4 py-2 text-slate-100 placeholder-slate-500 focus:outline-none focus:border-sky-500 focus:ring-1 focus:ring-sky-500 transition text-sm"
>
</div>
</div>
<!-- My Lists Table -->
<div id="myListsSection" class="space-y-2">
<h2 class="text-lg font-semibold text-slate-300">My Lists</h2>
<div class="bg-slate-800 rounded-lg border border-slate-700 overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-slate-700 bg-slate-900">
<th class="px-6 py-3 text-left font-semibold text-slate-300">Name</th>
<th class="px-6 py-3 text-left font-semibold text-slate-300">Slug</th>
<th class="px-6 py-3 text-left font-semibold text-slate-300">URLs</th>
<th class="px-6 py-3 text-left font-semibold text-slate-300">Visibility</th>
<th class="px-6 py-3 text-right font-semibold text-slate-300">Actions</th>
</tr>
</thead>
<tbody id="listContainer" class="divide-y divide-slate-700">
<!-- Loading State -->
<tr>
<td colspan="5" class="px-6 py-8 text-center text-slate-400">Loading lists...</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Empty State (hidden by default) -->
<div id="emptyState" class="hidden bg-slate-800 rounded-lg border border-slate-700 p-12 text-center">
<div class="text-4xl mb-3">📝</div>
<p class="text-slate-400 mb-4 text-sm">No lists yet. Create one to get started!</p>
<a href="{{ url_for('create_list') }}" class="inline-block bg-sky-600 hover:bg-sky-700 text-white font-semibold py-2 px-6 rounded transition duration-200 text-sm">
Create Your First List
</a>
</div>
<!-- Public Lists Section -->
<div id="publicListsSection" class="space-y-2 mt-8">
<h2 class="text-lg font-semibold text-slate-300">Public Lists (Search Results)</h2>
<div class="bg-slate-800 rounded-lg border border-slate-700 overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-slate-700 bg-slate-900">
<th class="px-6 py-3 text-left font-semibold text-slate-300">Name</th>
<th class="px-6 py-3 text-left font-semibold text-slate-300">Created By</th>
<th class="px-6 py-3 text-left font-semibold text-slate-300">URLs</th>
<th class="px-6 py-3 text-left font-semibold text-slate-300">Updated</th>
<th class="px-6 py-3 text-right font-semibold text-slate-300">Action</th>
</tr>
</thead>
<tbody id="publicListContainer" class="divide-y divide-slate-700">
<!-- Loading State -->
<tr>
<td colspan="5" class="px-6 py-8 text-center text-slate-400">Search public lists by entering a domain...</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
if (!isAuthenticated()) {
window.location.href = "{{ url_for('login') }}";
return;
}
loadLists();
});
let allLists = [];
function loadLists() {
const token = localStorage.getItem('access_token');
fetch('/api/url-lists/', {
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(response => {
if (response.status === 401) {
localStorage.removeItem('access_token');
window.location.href = "{{ url_for('login') }}";
return;
}
return response.json();
})
.then(lists => {
allLists = lists || [];
renderTable();
})
.catch(error => {
console.error('Error loading lists:', error);
document.getElementById('listContainer').innerHTML = `
<div class="p-8 text-center">
<p class="text-red-400 text-sm">Failed to load lists</p>
</div>
`;
});
}
function renderTable() {
const container = document.getElementById('listContainer');
const myListsSection = document.getElementById('myListsSection');
const emptyState = document.getElementById('emptyState');
if (!allLists || allLists.length === 0) {
myListsSection.classList.add('hidden');
emptyState.classList.remove('hidden');
return;
}
myListsSection.classList.remove('hidden');
emptyState.classList.add('hidden');
// Create table rows
let html = '';
allLists.forEach(list => {
const urlCount = list.urls.split('\n').filter(u => u.trim()).length;
const visibilityBadge = list.is_public ? '<span class="inline-block bg-green-900 text-green-300 text-xs px-2 py-1 rounded">🌐 Public</span>' : '<span class="inline-block bg-slate-700 text-slate-300 text-xs px-2 py-1 rounded">🔒 Private</span>';
const visibilityToggle = `<button onclick="togglePublic('${list.id}', ${list.is_public})" class="text-xs py-1 px-2 rounded ${list.is_public ? 'bg-green-900 hover:bg-green-800 text-green-300' : 'bg-slate-700 hover:bg-slate-600 text-slate-300'} transition">${list.is_public ? 'Make Private' : 'Make Public'}</button>`;
html += `
<tr class="hover:bg-slate-700 transition">
<td class="px-6 py-4 font-semibold text-slate-100">${escapeHtml(list.name)}</td>
<td class="px-6 py-4">
<span class="text-xs font-mono bg-slate-900 px-2 py-1 rounded inline-block text-sky-400">${escapeHtml(list.unique_slug)}</span>
</td>
<td class="px-6 py-4 text-slate-300">${urlCount} URL${urlCount !== 1 ? 's' : ''}</td>
<td class="px-6 py-4">${visibilityBadge}</td>
<td class="px-6 py-4 text-right">
<div class="flex gap-2 justify-end flex-wrap">
<a href="{{ url_for('list_read', slug='') }}${encodeURIComponent(list.unique_slug)}" target="_blank" class="inline-flex items-center justify-center w-8 h-8 bg-slate-700 hover:bg-sky-600 rounded transition duration-200 text-sm" title="View">
👁️
</a>
<button onclick="editList('${list.id}')" class="inline-flex items-center justify-center w-8 h-8 bg-slate-700 hover:bg-slate-600 rounded transition duration-200 text-sm" title="Edit">
✏️
</button>
<button onclick="deleteList('${list.id}')" class="inline-flex items-center justify-center w-8 h-8 bg-slate-700 hover:bg-red-600 rounded transition duration-200 text-sm" title="Delete">
🗑️
</button>
</div>
</td>
</tr>
`;
});
container.innerHTML = html;
</button>
<button onclick="deleteList('${list.id}')" class="inline-flex items-center justify-center w-8 h-8 bg-slate-700 hover:bg-red-600 rounded transition duration-200 text-sm" title="Delete">
🗑
</button>
</div>
</td>
</tr>
`;
});
container.innerHTML = html;
}
function escapeHtml(text) {
const map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return text.replace(/[&<>"']/g, m => map[m]);
}
// Search functionality
document.getElementById('searchInput').addEventListener('input', (e) => {
const query = e.target.value.toLowerCase();
const container = document.getElementById('listContainer');
if (!query) {
renderTable();
return;
}
const filtered = allLists.filter(list =>
list.name.toLowerCase().includes(query) ||
list.unique_slug.toLowerCase().includes(query)
);
if (filtered.length === 0) {
container.innerHTML = `
<tr>
<td colspan="4" class="px-6 py-8 text-center text-slate-400">No lists found matching "${escapeHtml(query)}"</td>
</tr>
`;
return;
}
// Temporarily set allLists for rendering
const temp = allLists;
allLists = filtered;
renderTable();
allLists = temp;
});
function editList(listId) {
const token = localStorage.getItem('access_token');
fetch(`/api/url-lists/${listId}`, {
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(response => response.json())
.then(list => {
// Store list data and redirect to create page for editing
sessionStorage.setItem('editList', JSON.stringify({
id: list.id,
name: list.name,
unique_slug: list.unique_slug,
urls: list.urls
}));
window.location.href = '{{ url_for("create_list") }}';
});
}
function deleteList(listId) {
if (!confirm('Are you sure you want to delete this list? This action cannot be undone.')) return;
const token = localStorage.getItem('access_token');
fetch(`/api/url-lists/${listId}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(response => {
if (response.ok) {
loadLists();
} else {
alert('Failed to delete list');
}
});
}
</script>
{% endblock %}