Update style on desktop browser

This commit is contained in:
2025-07-01 20:48:25 +10:00
parent 1320db9bd3
commit fc00453b6b
+448 -233
View File
@@ -1,7 +1,221 @@
{% extends 'base.html' %}
{% load i18n %}
{% block extra_css %}
<style>
/* Desktop-specific responsive improvements */
@media (min-width: 1024px) {
.main-content-grid {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 2rem;
}
.left-column {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.right-column {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.random-images-desktop {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.random-image-container {
aspect-ratio: 4/3;
max-height: 200px;
}
.section-title {
font-size: 0.875rem;
font-weight: 600;
color: #374151;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 0.75rem;
}
.popular-links-desktop {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
}
.latest-posts-desktop {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
.latest-post-card {
padding: 1rem;
background: white;
border-radius: 0.5rem;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
transition: box-shadow 0.2s;
}
.latest-post-card:hover {
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
}
/* Mobile and tablet - keep original layout */
@media (max-width: 1023px) {
.desktop-only {
display: none;
}
.mobile-layout {
display: block;
}
}
/* Desktop - use new layout */
@media (min-width: 1024px) {
.mobile-layout {
display: none;
}
.desktop-only {
display: block;
}
}
</style>
{% endblock %}
{% block content %}
<!-- Desktop Layout (1024px and up) -->
<div class="desktop-only">
<div class="main-content-grid">
<!-- Left Column - Main Content -->
<div class="left-column">
<!-- Popular Links Section -->
<div>
<h2 class="section-title">{% trans "Popular Links" %}</h2>
<div class="popular-links-desktop">
{% for item in popular_links %}
<div class="rounded-lg shadow-md overflow-hidden" style="background-color: {{ item.color }};">
<div class="p-4">
<a href="{% url 'redirect_to_original' item.link.alias %}" class="text-white hover:text-gray-200 font-semibold text-lg block truncate">
{{ item.link.alias }}
</a>
<p class="text-white text-sm mt-2 truncate">{% trans "Clicks" %}: {{ item.link.click_count }}</p>
</div>
</div>
{% empty %}
<p class="text-gray-600 col-span-full">{% trans "No popular links available" %}</p>
{% endfor %}
</div>
</div>
<!-- Latest Posts Section -->
<div>
<div class="flex justify-between items-center mb-3">
<h2 class="section-title mb-0">{% trans "Latest Posts" %}</h2>
<a href="{% url 'post-list' %}" class="text-blue-600 hover:text-blue-800 text-sm font-medium">{% trans "View all posts" %} →</a>
</div>
{% if latest_posts %}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{% for post in latest_posts %}
<div class="latest-post-card">
<h3 class="font-semibold text-gray-800 text-base mb-2 line-clamp-2">
<a href="{{ post.get_absolute_url }}" class="hover:text-blue-600 transition-colors">
{{ post.title }}
</a>
</h3>
<div class="flex items-center justify-between text-xs text-gray-500">
<span>{{ post.created_at|date:"M d, Y" }}</span>
{% if post.tags.all %}
<div class="flex flex-wrap gap-1">
{% for tag in post.tags.all|slice:":1" %}
<span class="bg-gray-100 text-gray-600 px-2 py-1 rounded-full text-xs">{{ tag.name }}</span>
{% endfor %}
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="bg-gray-50 rounded-lg p-4 text-center">
<p class="text-gray-600 text-sm">{% trans "No posts available yet" %}</p>
<a href="{% url 'post-create' %}" class="inline-block mt-2 text-blue-600 hover:text-blue-800 font-medium text-sm">{% trans "Create your first post" %}</a>
</div>
{% endif %}
</div>
<!-- Pinned Mini Apps Section -->
<div>
<h2 class="section-title">{% trans "Pinned Mini Apps" %}</h2>
<div class="popular-links-desktop">
{% for app in mini_apps %}
<div class="rounded-lg shadow-md overflow-hidden" style="background-color: {{ app.color }};">
<div class="p-4">
{% if app.url != '#' %}
<a href="{% url app.url %}" class="text-white hover:text-gray-200 font-semibold text-lg block truncate">
{{ app.name }}
</a>
{% else %}
<span class="text-white font-semibold text-lg block truncate cursor-default">
{{ app.name }}
</span>
{% endif %}
<p class="text-white text-sm mt-2 truncate">{{ app.description }}</p>
</div>
</div>
{% empty %}
<p class="text-gray-600 col-span-full">{% trans "No mini apps available" %}</p>
{% endfor %}
</div>
</div>
</div>
<!-- Right Column - Sidebar -->
<div class="right-column">
<!-- Random Images Section -->
<div>
<h2 class="section-title">{% trans "Random Images" %}</h2>
<div class="random-images-desktop">
<!-- Random Image Box 1 -->
<div class="random-image-container bg-gray-100 rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-200">
<img
src="/api/images/random/400/300/?fit=crop&v={{ timestamp }}"
alt="{% trans 'Random Image' %}"
class="w-full h-full object-cover cursor-pointer hover:scale-105 transition-transform duration-200 random-image"
data-box="1"
onclick="refreshRandomImage(this)"
loading="lazy"
/>
</div>
<!-- Random Image Box 2 -->
<div class="random-image-container bg-gray-100 rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-200">
<img
src="/api/images/random/400/300/?fit=crop&v={{ timestamp }}&offset=1"
alt="{% trans 'Random Image' %}"
class="w-full h-full object-cover cursor-pointer hover:scale-105 transition-transform duration-200 random-image"
data-box="2"
onclick="refreshRandomImage(this)"
loading="lazy"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Mobile/Tablet Layout (below 1024px) - Original Layout -->
<div class="mobile-layout">
<!-- Popular Links Section -->
<div class="mb-6">
<h2 class="text-lg font-semibold text-gray-700 mb-3 uppercase tracking-wide text-sm">{% trans "Popular Links" %}</h2>
@@ -113,253 +327,254 @@
{% endfor %}
</div>
</div>
</div>
<div class="bg-white shadow-md rounded-lg overflow-hidden">
<div class="p-4 sm:p-6 bg-gray-50 border-b border-gray-200">
<h1 class="text-lg font-semibold text-gray-700 mb-3 uppercase tracking-wide text-sm">{% trans "Link List" %}</h1>
<div class="flex flex-col sm:flex-row items-start sm:items-center justify-between space-y-4 sm:space-y-0">
<div class="w-full sm:w-64 relative">
<input type="text" id="search-alias-input" placeholder="{% trans 'Search links...' %}" class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
<svg class="h-4 w-4 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd" />
</svg>
</div>
</div>
<a href="{% url 'link_create' %}" class="w-full sm:w-auto bg-blue-500 hover:bg-blue-600 text-white text-sm font-medium py-2 px-4 rounded inline-flex items-center justify-center transition duration-150 ease-in-out">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
<!-- Shared Link List Section (both layouts) -->
<div class="bg-white shadow-md rounded-lg overflow-hidden">
<div class="p-4 sm:p-6 bg-gray-50 border-b border-gray-200">
<h1 class="text-lg font-semibold text-gray-700 mb-3 uppercase tracking-wide text-sm">{% trans "Link List" %}</h1>
<div class="flex flex-col sm:flex-row items-start sm:items-center justify-between space-y-4 sm:space-y-0">
<div class="w-full sm:w-64 relative">
<input type="text" id="search-alias-input" placeholder="{% trans 'Search links...' %}" class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
<svg class="h-4 w-4 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd" />
</svg>
{% trans "New Link" %}
</a>
</div>
</div>
<a href="{% url 'link_create' %}" class="w-full sm:w-auto bg-blue-500 hover:bg-blue-600 text-white text-sm font-medium py-2 px-4 rounded inline-flex items-center justify-center transition duration-150 ease-in-out">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
{% trans "New Link" %}
</a>
</div>
<form method="post" action="{% url 'delete_selected' %}">
{% csrf_token %}
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-3 py-2 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<input type="checkbox" id="select-all" class="rounded border-gray-300 text-blue-600 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50">
</th>
<th scope="col" class="px-3 py-2 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<a href="?sort=alias&order={% if current_sort == 'alias' and current_order == 'asc' %}desc{% else %}asc{% endif %}" class="flex items-center">
{% trans "Alias" %}
{% if current_sort == 'alias' %}
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M{% if current_order == 'asc' %}19 9l-7 7-7-7{% else %}5 15l7-7 7 7{% endif %}"></path>
</svg>
{% endif %}
</a>
</th>
<th scope="col" class="px-3 py-2 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider hidden sm:table-cell">
<a href="?sort=link_type&order={% if current_sort == 'link_type' and current_order == 'asc' %}desc{% else %}asc{% endif %}" class="flex items-center">
{% trans "Type" %}
{% if current_sort == 'link_type' %}
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M{% if current_order == 'asc' %}19 9l-7 7-7-7{% else %}5 15l7-7 7 7{% endif %}"></path>
</svg>
{% endif %}
</a>
</th>
<th scope="col" class="px-3 py-2 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider hidden md:table-cell">
<a href="?sort=original_url&order={% if current_sort == 'original_url' and current_order == 'asc' %}desc{% else %}asc{% endif %}" class="flex items-center">
{% trans "Original URL" %}
{% if current_sort == 'original_url' %}
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M{% if current_order == 'asc' %}19 9l-7 7-7-7{% else %}5 15l7-7 7 7{% endif %}"></path>
</svg>
{% endif %}
</a>
</th>
<th scope="col" class="px-3 py-2 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider hidden sm:table-cell">
<a href="?sort=clicks&order={% if current_sort == 'clicks' and current_order == 'asc' %}desc{% else %}asc{% endif %}" class="flex items-center">
{% trans "Clicks" %}
{% if current_sort == 'clicks' %}
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M{% if current_order == 'asc' %}19 9l-7 7-7-7{% else %}5 15l7-7 7 7{% endif %}"></path>
</svg>
{% endif %}
</a>
</th>
<th scope="col" class="px-3 py-2 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{% trans "Actions" %}</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200" id="links-table-body">
{% for link in links %}
<tr class="link-row">
<td class="px-3 py-2 sm:px-6 sm:py-4 whitespace-nowrap">
<input type="checkbox" name="selected_links" value="{{ link.id }}" class="rounded border-gray-300 text-blue-600 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50">
</td>
<td class="px-3 py-2 sm:px-6 sm:py-4 whitespace-nowrap">
<a href="{% url 'redirect_to_original' link.alias %}" target="_blank" class="text-blue-600 hover:text-blue-900">{{ link.alias }}</a>
</td>
<td class="px-3 py-2 sm:px-6 sm:py-4 whitespace-nowrap hidden sm:table-cell">
<span class="inline-flex items-center">
{% if '{' in link.original_url and '}' in link.original_url %}
<i class="fas fa-magic text-purple-500"></i>
<span class="ml-2 text-sm text-purple-600">Template</span>
{% elif link.link_type == 'LINK' %}
<i class="fas fa-link text-blue-500"></i>
<span class="ml-2 text-sm text-blue-600">Link</span>
{% else %}
<i class="fas fa-code text-green-500"></i>
<span class="ml-2 text-sm text-green-600">Custom</span>
{% endif %}
</span>
</td>
<td class="px-3 py-2 sm:px-6 sm:py-4 whitespace-nowrap hidden md:table-cell">
<div class="truncate max-w-xs" title="{{ link.original_url }}">
<a href="{% url 'redirect_to_original' link.alias %}"
class="text-blue-600 hover:text-blue-800 break-all"
target="_blank" rel="noopener noreferrer">
{{ link.original_url }}
</a>
</div>
</td>
<td class="px-3 py-2 sm:px-6 sm:py-4 whitespace-nowrap hidden sm:table-cell">{{ link.click_count }}</td>
<td class="px-3 py-2 sm:px-6 sm:py-4 whitespace-nowrap text-sm font-medium">
<div class="flex space-x-2">
<a href="{% url 'link_detail' link.id %}"
class="p-1 text-blue-600 hover:text-blue-800 hover:bg-blue-50 rounded"
title="{% trans 'View Details' %}">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
</a>
<a href="{% url 'link_update' link.id %}"
class="p-1 text-green-600 hover:text-green-800 hover:bg-green-50 rounded"
title="{% trans 'Edit' %}">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
</a>
<a href="{% url 'link_delete' link.id %}"
class="p-1 text-red-600 hover:text-red-800 hover:bg-red-50 rounded"
title="{% trans 'Delete' %}">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
</svg>
</a>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="6" class="px-3 py-2 sm:px-6 sm:py-4 whitespace-nowrap text-center text-gray-500">{% trans "No links available" %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="px-6 py-4 bg-gray-50 border-t border-gray-200">
<button type="submit" class="bg-red-500 hover:bg-red-600 text-white font-bold py-2 px-4 rounded">{% trans "Delete Selected" %}</button>
</div>
</form>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
<form method="post" action="{% url 'delete_selected' %}">
{% csrf_token %}
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-3 py-2 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<input type="checkbox" id="select-all" class="rounded border-gray-300 text-blue-600 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50">
</th>
<th scope="col" class="px-3 py-2 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<a href="?sort=alias&order={% if current_sort == 'alias' and current_order == 'asc' %}desc{% else %}asc{% endif %}" class="flex items-center">
{% trans "Alias" %}
{% if current_sort == 'alias' %}
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M{% if current_order == 'asc' %}19 9l-7 7-7-7{% else %}5 15l7-7 7 7{% endif %}"></path>
</svg>
{% endif %}
</a>
</th>
<th scope="col" class="px-3 py-2 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider hidden sm:table-cell">
<a href="?sort=link_type&order={% if current_sort == 'link_type' and current_order == 'asc' %}desc{% else %}asc{% endif %}" class="flex items-center">
{% trans "Type" %}
{% if current_sort == 'link_type' %}
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M{% if current_order == 'asc' %}19 9l-7 7-7-7{% else %}5 15l7-7 7 7{% endif %}"></path>
</svg>
{% endif %}
</a>
</th>
<th scope="col" class="px-3 py-2 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider hidden md:table-cell">
<a href="?sort=original_url&order={% if current_sort == 'original_url' and current_order == 'asc' %}desc{% else %}asc{% endif %}" class="flex items-center">
{% trans "Original URL" %}
{% if current_sort == 'original_url' %}
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M{% if current_order == 'asc' %}19 9l-7 7-7-7{% else %}5 15l7-7 7 7{% endif %}"></path>
</svg>
{% endif %}
</a>
</th>
<th scope="col" class="px-3 py-2 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider hidden sm:table-cell">
<a href="?sort=clicks&order={% if current_sort == 'clicks' and current_order == 'asc' %}desc{% else %}asc{% endif %}" class="flex items-center">
{% trans "Clicks" %}
{% if current_sort == 'clicks' %}
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M{% if current_order == 'asc' %}19 9l-7 7-7-7{% else %}5 15l7-7 7 7{% endif %}"></path>
</svg>
{% endif %}
</a>
</th>
<th scope="col" class="px-3 py-2 sm:px-6 sm:py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{% trans "Actions" %}</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200" id="links-table-body">
{% for link in links %}
<tr class="link-row">
<td class="px-3 py-2 sm:px-6 sm:py-4 whitespace-nowrap">
<input type="checkbox" name="selected_links" value="{{ link.id }}" class="rounded border-gray-300 text-blue-600 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50">
</td>
<td class="px-3 py-2 sm:px-6 sm:py-4 whitespace-nowrap">
<a href="{% url 'redirect_to_original' link.alias %}" target="_blank" class="text-blue-600 hover:text-blue-900">{{ link.alias }}</a>
</td>
<td class="px-3 py-2 sm:px-6 sm:py-4 whitespace-nowrap hidden sm:table-cell">
<span class="inline-flex items-center">
{% if '{' in link.original_url and '}' in link.original_url %}
<i class="fas fa-magic text-purple-500"></i>
<span class="ml-2 text-sm text-purple-600">Template</span>
{% elif link.link_type == 'LINK' %}
<i class="fas fa-link text-blue-500"></i>
<span class="ml-2 text-sm text-blue-600">Link</span>
{% else %}
<i class="fas fa-code text-green-500"></i>
<span class="ml-2 text-sm text-green-600">Custom</span>
{% endif %}
</span>
</td>
<td class="px-3 py-2 sm:px-6 sm:py-4 whitespace-nowrap hidden md:table-cell">
<div class="truncate max-w-xs" title="{{ link.original_url }}">
<a href="{% url 'redirect_to_original' link.alias %}"
class="text-blue-600 hover:text-blue-800 break-all"
target="_blank" rel="noopener noreferrer">
{{ link.original_url }}
</a>
</div>
</td>
<td class="px-3 py-2 sm:px-6 sm:py-4 whitespace-nowrap hidden sm:table-cell">{{ link.click_count }}</td>
<td class="px-3 py-2 sm:px-6 sm:py-4 whitespace-nowrap text-sm font-medium">
<div class="flex space-x-2">
<a href="{% url 'link_detail' link.id %}"
class="p-1 text-blue-600 hover:text-blue-800 hover:bg-blue-50 rounded"
title="{% trans 'View Details' %}">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
</a>
<a href="{% url 'link_update' link.id %}"
class="p-1 text-green-600 hover:text-green-800 hover:bg-green-50 rounded"
title="{% trans 'Edit' %}">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
</a>
<a href="{% url 'link_delete' link.id %}"
class="p-1 text-red-600 hover:text-red-800 hover:bg-red-50 rounded"
title="{% trans 'Delete' %}">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
</svg>
</a>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="6" class="px-3 py-2 sm:px-6 sm:py-4 whitespace-nowrap text-center text-gray-500">{% trans "No links available" %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="px-6 py-4 bg-gray-50 border-t border-gray-200">
<button type="submit" class="bg-red-500 hover:bg-red-600 text-white font-bold py-2 px-4 rounded">{% trans "Delete Selected" %}</button>
</div>
</form>
</div>
const searchInput = document.getElementById('search-alias-input');
const rows = document.querySelectorAll('.link-row');
const tbody = document.getElementById('links-table-body');
<script>
document.addEventListener('DOMContentLoaded', function() {
const searchInput = document.getElementById('search-alias-input');
const rows = document.querySelectorAll('.link-row');
const tbody = document.getElementById('links-table-body');
if (searchInput) {
if (searchInput) {
function performSearch() {
const searchTerm = searchInput.value.toLowerCase().trim();
let visibleCount = 0;
function performSearch() {
const searchTerm = searchInput.value.toLowerCase().trim();
let visibleCount = 0;
rows.forEach((row, index) => {
const aliasElement = row.querySelector('td:nth-child(2) a');
if (aliasElement) {
const alias = aliasElement.textContent.toLowerCase();
const shouldShow = alias.includes(searchTerm);
row.style.display = shouldShow ? '' : 'none';
if (shouldShow) visibleCount++;
} else {
console.error(`${index + 1} 行没有找到别名元素`);
}
});
const noResultsRow = document.getElementById('no-results-row');
if (visibleCount === 0) {
if (!noResultsRow) {
const newRow = document.createElement('tr');
newRow.id = 'no-results-row';
newRow.innerHTML = `<td colspan="8" class="px-6 py-4 whitespace-nowrap text-center text-gray-500">{% trans "No matching links found" %}</td>`;
tbody.appendChild(newRow);
}
} else if (noResultsRow) {
noResultsRow.remove();
rows.forEach((row, index) => {
const aliasElement = row.querySelector('td:nth-child(2) a');
if (aliasElement) {
const alias = aliasElement.textContent.toLowerCase();
const shouldShow = alias.includes(searchTerm);
row.style.display = shouldShow ? '' : 'none';
if (shouldShow) visibleCount++;
} else {
console.error(`${index + 1} 行没有找到别名元素`);
}
});
const noResultsRow = document.getElementById('no-results-row');
if (visibleCount === 0) {
if (!noResultsRow) {
const newRow = document.createElement('tr');
newRow.id = 'no-results-row';
newRow.innerHTML = `<td colspan="8" class="px-6 py-4 whitespace-nowrap text-center text-gray-500">{% trans "No matching links found" %}</td>`;
tbody.appendChild(newRow);
}
} else if (noResultsRow) {
noResultsRow.remove();
}
// 使用 'keyup' 事件
searchInput.addEventListener('keyup', performSearch);
} else {
console.error('未找到搜索输入框');
}
});
</script>
{% block extra_js %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const utcTimes = document.querySelectorAll('.utc-time');
utcTimes.forEach(function(element) {
const utcTime = new Date(element.dataset.utc);
const localTime = utcTime.toLocaleString();
element.textContent = localTime;
});
});
// Random image refresh functionality
function refreshRandomImage(imgElement) {
const timestamp = Date.now();
// Determine if it's mobile or desktop for different sizes
const isMobile = window.innerWidth < 640; // sm breakpoint
let newUrl;
if (imgElement.dataset.box === '1') {
// First image box
const size = isMobile ? '250' : '300';
newUrl = `/api/images/random/${size}/${size}/?fit=crop&v=${timestamp}`;
} else {
// Second image box
const size = isMobile ? '280' : '400';
newUrl = `/api/images/random/${size}/${size}/?fit=crop&v=${timestamp}&offset=1`;
}
// Add loading animation
imgElement.style.opacity = '0.5';
imgElement.style.transition = 'opacity 0.3s ease';
const newImg = new Image();
newImg.onload = function() {
imgElement.src = newImg.src;
imgElement.style.opacity = '1';
};
newImg.onerror = function() {
// Fallback - just update timestamp on current URL
const currentUrl = new URL(imgElement.src);
currentUrl.searchParams.set('v', timestamp);
imgElement.src = currentUrl.toString();
imgElement.style.opacity = '1';
};
newImg.src = newUrl;
// 使用 'keyup' 事件
searchInput.addEventListener('keyup', performSearch);
} else {
console.error('未找到搜索输入框');
}
</script>
{% endblock %}
});
</script>
{% block extra_js %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const utcTimes = document.querySelectorAll('.utc-time');
utcTimes.forEach(function(element) {
const utcTime = new Date(element.dataset.utc);
const localTime = utcTime.toLocaleString();
element.textContent = localTime;
});
});
// Random image refresh functionality
function refreshRandomImage(img) {
const box = img.getAttribute('data-box');
const timestamp = new Date().getTime();
const width = img.offsetWidth || 500;
const height = img.offsetHeight || 500;
const newUrl = `/api/images/random/${width}/${height}/?fit=crop&v=${timestamp}&box=${box}`;
// Create new image to preload
const newImg = new Image();
newImg.onload = function() {
img.style.opacity = '0.5';
setTimeout(() => {
img.src = newUrl;
img.style.opacity = '1';
}, 150);
};
newImg.onerror = function() {
console.error('Failed to load new random image');
// Fallback - just update timestamp on current URL
const currentUrl = new URL(img.src);
currentUrl.searchParams.set('v', timestamp);
img.src = currentUrl.toString();
img.style.opacity = '1';
};
newImg.src = newUrl;
}
// Select all functionality
document.getElementById('select-all').addEventListener('change', function() {
const checkboxes = document.querySelectorAll('input[name="selected_links"]');
checkboxes.forEach(checkbox => {
checkbox.checked = this.checked;
});
});
</script>
{% endblock %}
{% endblock %}