🎨 Improved edit links page with responsive card grid layout
📱 Mobile & Desktop Optimization: - Responsive grid layout: 1 card (mobile) → 2 cards (tablet) → 3 cards (desktop) → 4 cards (large screens) - Better card design with improved spacing and hover effects - Enhanced mobile experience with optimized touch targets 🔧 UI/UX Improvements: - Modern card-based layout instead of stacked list - Website logos with fallback icons for better visual recognition - Improved button placement and iconography - Better responsive breakpoints for different screen sizes ✨ Visual Enhancements: - Smooth hover animations and shadow effects - Better logo positioning with placeholder icons - Optimized spacing and typography for mobile devices - Professional card design with rounded corners and subtle shadows This update provides a much cleaner and more organized view of links, especially on mobile devices where the cards stack properly and buttons are easily accessible.
This commit is contained in:
@@ -135,20 +135,41 @@
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.links-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 15px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.link-item {
|
||||
background: white;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 8px;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
margin-bottom: 15px;
|
||||
margin-bottom: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.link-item:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.link-item.editing {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.2);
|
||||
}
|
||||
|
||||
.link-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.link-content {
|
||||
@@ -156,9 +177,25 @@
|
||||
}
|
||||
|
||||
.link-logo {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 6px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
flex-shrink: 0;
|
||||
object-fit: cover;
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.link-icon-placeholder {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -253,12 +290,93 @@
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
body {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.container {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 1.8em;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 20px;
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.links-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.link-item {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.link-header {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.link-logo,
|
||||
.link-icon-placeholder {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
.link-title {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.link-actions {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.link-actions .btn-small {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.page-actions {
|
||||
flex-direction: column;
|
||||
padding: 15px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.page-actions .btn {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 769px) and (max-width: 1024px) {
|
||||
.links-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1025px) {
|
||||
.links-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1400px) {
|
||||
.links-grid {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -310,28 +428,35 @@
|
||||
|
||||
<div class="links-section">
|
||||
<h2>Current Links ({{ page.links|length }})</h2>
|
||||
<div id="links-container">
|
||||
<div class="links-grid" id="links-container">
|
||||
{% if page.links %}
|
||||
{% for link in page.links %}
|
||||
<div class="link-item" data-link-id="{{ link.id }}">
|
||||
<div class="link-content">
|
||||
<div class="link-display">
|
||||
<div class="link-title">{{ link.title }}</div>
|
||||
{% if link.description %}
|
||||
<div class="link-description">{{ link.description }}</div>
|
||||
{% endif %}
|
||||
<div class="link-url" data-url="{{ link.url }}">{{ link.url }}</div>
|
||||
<div class="link-display">
|
||||
<div class="link-header">
|
||||
<div class="link-icon-placeholder" style="display: flex;">🔗</div>
|
||||
<img class="link-logo" src="" alt="" style="display: none;" onerror="this.style.display='none'">
|
||||
<div class="link-content">
|
||||
<div class="link-title">{{ link.title }}</div>
|
||||
{% if link.description %}
|
||||
<div class="link-description">{{ link.description }}</div>
|
||||
{% endif %}
|
||||
<div class="link-url" data-url="{{ link.url }}">{{ link.url }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if link.short_url %}
|
||||
<div class="short-url-display" style="margin-top: 8px; padding: 8px; background: #e3f2fd; border-radius: 5px; border-left: 3px solid #2196f3;">
|
||||
<div class="short-url-display" style="margin-top: 12px; padding: 10px; background: #e3f2fd; border-radius: 8px; border-left: 3px solid #2196f3;">
|
||||
<small style="color: #1976d2; font-weight: 600;">🔗 Short URL:</small>
|
||||
<br>
|
||||
<a href="{{ link.short_url }}" target="_blank" style="color: #1976d2; text-decoration: none; font-family: monospace;">{{ link.short_url }}</a>
|
||||
<button class="btn-copy" onclick="copyToClipboard('{{ link.short_url }}')" style="margin-left: 10px; padding: 2px 8px; background: #2196f3; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 0.8em;">Copy</button>
|
||||
<a href="{{ link.short_url }}" target="_blank" style="color: #1976d2; text-decoration: none; font-family: monospace; font-size: 0.9em;">{{ link.short_url }}</a>
|
||||
<button class="btn-copy" onclick="copyToClipboard('{{ link.short_url }}')" style="margin-left: 8px; padding: 3px 8px; background: #2196f3; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 0.8em;">Copy</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="link-actions">
|
||||
<button class="btn btn-small btn-secondary" onclick="editLink('{{ link.id }}')">Edit</button>
|
||||
<button class="btn btn-small btn-danger" onclick="deleteLink('{{ link.id }}')">Delete</button>
|
||||
<button class="btn btn-small btn-secondary" onclick="editLink('{{ link.id }}')">✏️ Edit</button>
|
||||
<button class="btn btn-small btn-danger" onclick="deleteLink('{{ link.id }}')">🗑️ Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -349,15 +474,14 @@
|
||||
<textarea class="edit-description">{{ link.description or '' }}</textarea>
|
||||
</div>
|
||||
<div class="link-actions">
|
||||
<button class="btn btn-small btn-success" onclick="saveLink('{{ link.id }}')">Save</button>
|
||||
<button class="btn btn-small btn-secondary" onclick="cancelEdit('{{ link.id }}')">Cancel</button>
|
||||
<button class="btn btn-small btn-success" onclick="saveLink('{{ link.id }}')">💾 Save</button>
|
||||
<button class="btn btn-small btn-secondary" onclick="cancelEdit('{{ link.id }}')">❌ Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
<img class="link-logo" src="" alt="" style="display: none;" onerror="this.style.display='none'">
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="empty-state">
|
||||
<div class="empty-state" style="grid-column: 1 / -1;">
|
||||
<div class="icon">📝</div>
|
||||
<h3>No links yet</h3>
|
||||
<p>Add your first link using the form on the left.</p>
|
||||
@@ -426,13 +550,26 @@
|
||||
function setLogosForLinks() {
|
||||
document.querySelectorAll('.link-url[data-url]').forEach(linkElement => {
|
||||
const url = linkElement.getAttribute('data-url');
|
||||
const logoImg = linkElement.closest('.link-item').querySelector('.link-logo');
|
||||
const linkItem = linkElement.closest('.link-item');
|
||||
const logoImg = linkItem.querySelector('.link-logo');
|
||||
const placeholder = linkItem.querySelector('.link-icon-placeholder');
|
||||
const logoSrc = getWebsiteLogo(url);
|
||||
|
||||
if (logoSrc && logoImg) {
|
||||
logoImg.src = logoSrc;
|
||||
logoImg.style.display = 'block';
|
||||
logoImg.alt = new URL(url.startsWith('http') ? url : 'https://' + url).hostname;
|
||||
|
||||
// Show logo and hide placeholder when loaded
|
||||
logoImg.onload = function() {
|
||||
logoImg.style.display = 'block';
|
||||
if (placeholder) placeholder.style.display = 'none';
|
||||
};
|
||||
|
||||
// Show placeholder if logo fails to load
|
||||
logoImg.onerror = function() {
|
||||
logoImg.style.display = 'none';
|
||||
if (placeholder) placeholder.style.display = 'flex';
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -432,7 +432,7 @@
|
||||
<div class="stat-label">Page Views</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-number">{{ page.links|sum(attribute='click_count') or 0 }}</div>
|
||||
<div class="stat-number">{{ page.links|sum(attribute='clicks') or 0 }}</div>
|
||||
<div class="stat-label">Total Clicks</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -500,7 +500,7 @@
|
||||
{% endif %}
|
||||
<div class="link-url">{{ link.short_url if link.short_url else link.url }}</div>
|
||||
<div style="margin-top: 10px; font-size: 0.85em; color: #666;">
|
||||
<strong>Clicks:</strong> {{ link.click_count or 0 }} |
|
||||
<strong>Clicks:</strong> {{ link.clicks or 0 }} |
|
||||
<strong>Added:</strong> {{ link.created_at or 'N/A' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user