final app for testing and deployment

This commit is contained in:
2025-07-16 20:45:12 +03:00
parent 729f64f411
commit e9a8f5e622
26 changed files with 1561 additions and 168 deletions

View File

@@ -141,12 +141,27 @@
border-radius: 8px;
padding: 20px;
margin-bottom: 15px;
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 15px;
}
.link-item.editing {
border-color: #667eea;
}
.link-content {
flex: 1;
}
.link-logo {
width: 32px;
height: 32px;
border-radius: 6px;
flex-shrink: 0;
}
.link-display {
display: block;
}
@@ -253,6 +268,13 @@
<div class="header">
<h1>✏️ Edit Links</h1>
<p>Manage your link collection: {{ page.title }}</p>
{% if page.short_url %}
<div style="margin-top: 15px; padding: 12px; background: rgba(255,255,255,0.2); border-radius: 8px; font-size: 0.9em;">
<strong>🔗 Page Short URL:</strong>
<a href="{{ page.short_url }}" target="_blank" style="color: #fff; text-decoration: underline;">{{ page.short_url }}</a>
<button onclick="copyToClipboard('{{ page.short_url }}')" style="margin-left: 10px; padding: 4px 8px; background: rgba(255,255,255,0.3); color: white; border: 1px solid rgba(255,255,255,0.5); border-radius: 3px; cursor: pointer; font-size: 0.8em;">Copy</button>
</div>
{% endif %}
</div>
<div class="alert alert-success" id="success-alert">
@@ -292,12 +314,21 @@
{% if page.links %}
{% for link in page.links %}
<div class="link-item" data-link-id="{{ link.id }}">
<div class="link-display">
<div class="link-title">{{ link.title }}</div>
{% if link.description %}
<div class="link-description">{{ link.description }}</div>
<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>
{% 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;">
<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>
</div>
{% endif %}
<div class="link-url">{{ link.url }}</div>
<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>
@@ -322,6 +353,7 @@
<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 %}
@@ -344,6 +376,87 @@
<script>
const pageId = '{{ page.id }}';
// Social media and website logo detection
function getWebsiteLogo(url) {
try {
const urlObj = new URL(url.startsWith('http') ? url : 'https://' + url);
const domain = urlObj.hostname.toLowerCase().replace('www.', '');
// Logo mapping for popular sites
const logoMap = {
'facebook.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/facebook.svg',
'instagram.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/instagram.svg',
'twitter.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/twitter.svg',
'x.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/x.svg',
'tiktok.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/tiktok.svg',
'youtube.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/youtube.svg',
'linkedin.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/linkedin.svg',
'pinterest.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/pinterest.svg',
'snapchat.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/snapchat.svg',
'whatsapp.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/whatsapp.svg',
'telegram.org': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/telegram.svg',
'discord.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/discord.svg',
'reddit.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/reddit.svg',
'github.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/github.svg',
'gmail.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/gmail.svg',
'google.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/google.svg',
'amazon.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/amazon.svg',
'apple.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/apple.svg',
'microsoft.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/microsoft.svg',
'spotify.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/spotify.svg',
'netflix.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/netflix.svg',
'twitch.tv': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/twitch.svg',
'dropbox.com': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/dropbox.svg',
'zoom.us': 'https://cdn.jsdelivr.net/npm/simple-icons@v9/icons/zoom.svg'
};
if (logoMap[domain]) {
return logoMap[domain];
}
// Fallback to favicon
return `https://www.google.com/s2/favicons?domain=${domain}&sz=64`;
} catch (e) {
return null;
}
}
// Set logos for existing links
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 logoSrc = getWebsiteLogo(url);
if (logoSrc && logoImg) {
logoImg.src = logoSrc;
logoImg.style.display = 'block';
logoImg.alt = new URL(url.startsWith('http') ? url : 'https://' + url).hostname;
}
});
}
// Initialize logos when page loads
document.addEventListener('DOMContentLoaded', setLogosForLinks);
// Copy to clipboard function
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(function() {
// Show temporary success message
const btn = event.target;
const originalText = btn.textContent;
btn.textContent = 'Copied!';
btn.style.background = '#4caf50';
setTimeout(() => {
btn.textContent = originalText;
btn.style.background = '#2196f3';
}, 1500);
}).catch(function(err) {
console.error('Could not copy text: ', err);
alert('Failed to copy to clipboard');
});
}
// Add new link
document.getElementById('add-link-form').addEventListener('submit', async function(e) {
e.preventDefault();
@@ -358,7 +471,11 @@
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ title, url, description })
body: JSON.stringify({
title,
url,
description
})
});
const result = await response.json();
@@ -399,7 +516,11 @@
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ title, url, description })
body: JSON.stringify({
title,
url,
description
})
});
const result = await response.json();