Files
urllists/static/templates/dashboard_old.html
2025-11-30 12:19:31 +00:00

284 lines
11 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. 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 - {{ 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 %}