CmdForge/src/smarttools/web/templates/dashboard/tools.html

235 lines
12 KiB
HTML

{% extends "dashboard/base.html" %}
{% set active_page = 'tools' %}
{% block title %}My Tools - SmartTools Dashboard{% endblock %}
{% block dashboard_header %}
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-gray-900">My Tools</h1>
<p class="mt-1 text-gray-600">Manage your published tools</p>
</div>
<a href="{{ url_for('web.docs', path='publishing') }}"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-indigo-600 rounded-md hover:bg-indigo-700">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
</svg>
Publish New Tool
</a>
</div>
{% endblock %}
{% block dashboard_content %}
{% if tools %}
<div class="bg-white rounded-lg border border-gray-200 overflow-hidden">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Tool
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Version
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Downloads
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Status
</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
Actions
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
{% for tool in tools %}
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="w-10 h-10 bg-indigo-100 rounded-lg flex items-center justify-center flex-shrink-0">
<svg class="w-5 h-5 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
</svg>
</div>
<div class="ml-4">
<a href="{{ url_for('web.tool_detail', owner=tool.owner, name=tool.name) }}"
class="text-sm font-medium text-gray-900 hover:text-indigo-600">
{{ tool.name }}
</a>
<p class="text-sm text-gray-500 truncate max-w-xs">{{ tool.description or 'No description' }}</p>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="text-sm text-gray-900">v{{ tool.version }}</span>
<p class="text-xs text-gray-500">{{ tool.published_at|timeago }}</p>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{{ tool.downloads|format_number }}
</td>
<td class="px-6 py-4 whitespace-nowrap">
{% if tool.deprecated %}
<span class="px-2 py-1 text-xs font-medium text-amber-800 bg-amber-100 rounded-full">
Deprecated
</span>
{% else %}
<span class="px-2 py-1 text-xs font-medium text-green-800 bg-green-100 rounded-full">
Active
</span>
{% endif %}
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<div class="flex items-center justify-end gap-2">
<a href="{{ url_for('web.tool_detail', owner=tool.owner, name=tool.name) }}"
class="text-indigo-600 hover:text-indigo-900">View</a>
<button type="button"
onclick="openDeprecateModal('{{ tool.owner }}', '{{ tool.name }}', {{ 'true' if tool.deprecated else 'false' }})"
class="text-gray-400 hover:text-amber-600">
{% if tool.deprecated %}Restore{% else %}Deprecate{% endif %}
</button>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Pending PRs Section -->
{% if pending_prs %}
<div class="mt-8">
<h2 class="text-lg font-semibold text-gray-900 mb-4">Pending Publications</h2>
<div class="bg-white rounded-lg border border-gray-200 overflow-hidden">
<ul class="divide-y divide-gray-200">
{% for pr in pending_prs %}
<li class="px-6 py-4 flex items-center justify-between">
<div>
<p class="text-sm font-medium text-gray-900">{{ pr.owner }}/{{ pr.name }} v{{ pr.version }}</p>
<p class="text-xs text-gray-500">Submitted {{ pr.created_at|timeago }}</p>
</div>
<div class="flex items-center gap-3">
<span class="px-2 py-1 text-xs font-medium text-blue-800 bg-blue-100 rounded-full">
{{ pr.status }}
</span>
<a href="{{ pr.pr_url }}"
target="_blank"
rel="noopener noreferrer"
class="text-sm text-indigo-600 hover:text-indigo-800">
View PR
</a>
</div>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
{% else %}
<!-- Empty State -->
<div class="bg-white rounded-lg border border-gray-200 text-center py-16">
<svg class="mx-auto w-16 h-16 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"/>
</svg>
<h3 class="mt-4 text-lg font-medium text-gray-900">No tools yet</h3>
<p class="mt-2 text-gray-600 max-w-sm mx-auto">
You haven't published any tools. Create your first tool and share it with the community.
</p>
<a href="{{ url_for('web.docs', path='publishing') }}"
class="mt-6 inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-indigo-600 rounded-md hover:bg-indigo-700">
Learn How to Publish
</a>
</div>
{% endif %}
<!-- Deprecate Modal -->
<div id="deprecate-modal" class="hidden fixed inset-0 z-50 overflow-y-auto" aria-modal="true" role="dialog">
<div class="min-h-screen px-4 text-center">
<div class="fixed inset-0 bg-black bg-opacity-50" onclick="closeDeprecateModal()"></div>
<div class="inline-block w-full max-w-md my-8 text-left align-middle bg-white shadow-xl rounded-lg">
<form id="deprecate-form" method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="owner" id="deprecate-owner">
<input type="hidden" name="name" id="deprecate-name">
<div class="p-6">
<h3 id="deprecate-title" class="text-lg font-semibold text-gray-900">Deprecate Tool</h3>
<p id="deprecate-desc" class="mt-2 text-sm text-gray-600">
Mark this tool as deprecated. Users will see a warning when viewing or installing it.
</p>
<div id="deprecate-fields" class="mt-4 space-y-4">
<div>
<label for="deprecated_message" class="block text-sm font-medium text-gray-700 mb-1">
Deprecation message (optional)
</label>
<textarea name="deprecated_message"
id="deprecated_message"
rows="2"
placeholder="e.g., This tool has been superseded by..."
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500"></textarea>
</div>
<div>
<label for="replacement" class="block text-sm font-medium text-gray-700 mb-1">
Replacement tool (optional)
</label>
<input type="text"
name="replacement"
id="replacement"
placeholder="owner/tool-name"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500">
</div>
</div>
</div>
<div class="bg-gray-50 px-6 py-4 rounded-b-lg flex justify-end gap-3">
<button type="button"
onclick="closeDeprecateModal()"
class="px-4 py-2 text-sm font-medium text-gray-700 hover:text-gray-900">
Cancel
</button>
<button type="submit"
id="deprecate-submit"
class="px-4 py-2 text-sm font-medium text-white bg-amber-600 rounded-md hover:bg-amber-700">
Deprecate
</button>
</div>
</form>
</div>
</div>
</div>
<script>
function openDeprecateModal(owner, name, isDeprecated) {
document.getElementById('deprecate-owner').value = owner;
document.getElementById('deprecate-name').value = name;
if (isDeprecated) {
document.getElementById('deprecate-title').textContent = 'Restore Tool';
document.getElementById('deprecate-desc').textContent = 'Remove the deprecation notice from this tool.';
document.getElementById('deprecate-fields').classList.add('hidden');
document.getElementById('deprecate-submit').textContent = 'Restore';
document.getElementById('deprecate-submit').classList.remove('bg-amber-600', 'hover:bg-amber-700');
document.getElementById('deprecate-submit').classList.add('bg-green-600', 'hover:bg-green-700');
document.getElementById('deprecate-form').action = `/api/v1/tools/${owner}/${name}/undeprecate`;
} else {
document.getElementById('deprecate-title').textContent = 'Deprecate Tool';
document.getElementById('deprecate-desc').textContent = 'Mark this tool as deprecated. Users will see a warning.';
document.getElementById('deprecate-fields').classList.remove('hidden');
document.getElementById('deprecate-submit').textContent = 'Deprecate';
document.getElementById('deprecate-submit').classList.remove('bg-green-600', 'hover:bg-green-700');
document.getElementById('deprecate-submit').classList.add('bg-amber-600', 'hover:bg-amber-700');
document.getElementById('deprecate-form').action = `/api/v1/tools/${owner}/${name}/deprecate`;
}
document.getElementById('deprecate-modal').classList.remove('hidden');
}
function closeDeprecateModal() {
document.getElementById('deprecate-modal').classList.add('hidden');
}
</script>
{% endblock %}