+
+
-
-
Výchozí ikona
-
Vyberte ikonu z výše uvedených
+
+
+
@@ -1125,6 +1157,9 @@ function updateBannerPreview() {
// No custom positioning, always right-aligned
}
+// Banner variables will be initialized in DOMContentLoaded
+let bannerVisible, bannerBgColor, bannerTextColor, bannerText, bannerTextAlign, bannerFontSize, bannerPadding, bannerMargin, bannerBorderRadius, bannerPreview;
+
// Initialize template object
let template = {
containerStyle: '',
@@ -1688,7 +1723,10 @@ async function saveApp(event) {
// Basic validation
const name = document.getElementById('appName').value.trim();
- const url = document.getElementById('appUrl').value.trim();
+ const url = document.getElementById('appLink').value.trim();
+ const description = document.getElementById('appDescription').value.trim();
+ const icon = document.getElementById('appIcon').value || 'fas fa-cube';
+ const color = document.getElementById('appColor').value || '#4a6cf7';
if (!name) {
showNotification('Název aplikace je povinný', 'error');
@@ -1700,30 +1738,17 @@ async function saveApp(event) {
return;
}
- // Get the selected icon class or generate a random one
- let iconClass = document.getElementById('appIconClass').value;
- if (!iconClass) {
- const icons = [
- 'fa-globe', 'fa-link', 'fa-external-link-alt', 'fa-cube', 'fa-box', 'fa-folder',
- 'fa-file', 'fa-archive', 'fa-database', 'fa-server', 'fa-network-wired', 'fa-sitemap'
- ];
- iconClass = icons[Math.floor(Math.random() * icons.length)];
- }
-
- // Generate a random color for the app
- const colors = ['blue', 'green', 'red', 'yellow', 'indigo', 'purple', 'pink', 'gray'];
- const randomColor = colors[Math.floor(Math.random() * colors.length)];
-
+ // Prepare form data
formData.append('name', name);
formData.append('url', url);
- formData.append('description', document.getElementById('appDescription').value.trim());
- formData.append('color', randomColor);
- formData.append('iconClass', iconClass);
+ formData.append('description', description);
+ formData.append('icon', icon);
+ formData.append('color', color);
- // Handle icon upload if a new file is selected
- const iconInput = document.getElementById('appIcon');
- if (iconInput.files.length > 0) {
- formData.append('icon', iconInput.files[0]);
+ // Handle custom icon file upload if selected
+ const customIconInput = document.getElementById('customIconInput');
+ if (customIconInput.files.length > 0) {
+ formData.append('iconFile', customIconInput.files[0]);
}
try {
@@ -1796,72 +1821,43 @@ async function editApp(appId) {
// Set form values
document.getElementById('appId').value = app.id;
document.getElementById('appName').value = app.name;
- document.getElementById('appUrl').value = app.url;
+ document.getElementById('appLink').value = app.url;
document.getElementById('appDescription').value = app.description || '';
document.getElementById('appModalTitle').textContent = 'Upravit aplikaci';
- // Update file name display
- const fileNameDisplay = document.getElementById('fileName');
- if (fileNameDisplay) {
- fileNameDisplay.textContent = app.icon ? 'Stávající soubor: ' + app.icon : 'Nebyl vybrán žádný soubor';
+ // Set color if exists
+ if (app.color) {
+ document.getElementById('appColor').value = app.color;
+ document.getElementById('appColorText').value = app.color;
}
// Show icon preview if exists
- const iconPreview = document.getElementById('appIconPreview');
+ const iconPreview = document.getElementById('customIconPreview');
+ const selectedIcon = document.getElementById('selectedIcon');
+
if (app.icon) {
- iconPreview.src = `/uploads/${app.icon}`;
- iconPreview.classList.remove('hidden');
+ if (app.icon.startsWith('http') || app.icon.startsWith('/')) {
+ iconPreview.src = app.icon;
+ iconPreview.classList.remove('hidden');
+ selectedIcon.classList.add('hidden');
+ } else {
+ iconPreview.src = `/uploads/${app.icon}`;
+ iconPreview.classList.remove('hidden');
+ selectedIcon.classList.add('hidden');
+ }
+ document.getElementById('appIcon').value = 'custom';
} else {
iconPreview.classList.add('hidden');
- }
-
- // Clear file input to allow re-selecting the same file
- const fileInput = document.getElementById('appIcon');
- if (fileInput) {
- fileInput.value = '';
+ selectedIcon.classList.remove('hidden');
+ document.getElementById('appIcon').value = '';
}
// Show the modal
document.getElementById('appModal').classList.remove('hidden');
} catch (error) {
- console.error('Chyba při načítání aplikace:', error);
- showNotification('Nepodařilo se načíst data aplikace', 'error');
- }
-}
-
-async function deleteApp(appId) {
- // Prevent deleting hardcoded apps
- if (appId && appId.startsWith('hardcoded-')) {
- showNotification('Tuto přednastavenou aplikaci nelze smazat', 'warning');
- return;
- }
-
- if (!confirm('Opravdu chcete tuto aplikaci smazat? Tato akce je nevratná.')) {
- return;
- }
-
- try {
- const response = await fetch(`/api/apps/${appId}`, {
- method: 'DELETE',
- headers: {
- 'Authorization': `Bearer ${localStorage.getItem('token')}`,
- 'Content-Type': 'application/json'
- }
- });
-
- if (!response.ok) {
- const error = await response.json();
- throw new Error(error.message || 'Nepodařilo se smazat aplikaci');
- }
-
- // Reload only dynamic apps
- await loadDynamicApps();
- showNotification('Aplikace byla úspěšně smazána', 'success');
-
- } catch (error) {
- console.error('Chyba při mazání aplikace:', error);
- showNotification(error.message || 'Nepodařilo se smazat aplikaci', 'error');
+ console.error('Error loading app:', error);
+ showNotification(error.message || 'Nastala chyba při načítání aplikace', 'error');
}
}
@@ -1877,19 +1873,28 @@ function openAddAppModal() {
document.getElementById('appModalTitle').textContent = 'Přidat aplikaci';
// Reset file input and preview
- const fileInput = document.getElementById('appIcon');
- const fileNameDisplay = document.getElementById('fileName');
- const previewImg = document.getElementById('appIconPreview');
+ const customIconInput = document.getElementById('customIconInput');
+ const customIconPreview = document.getElementById('customIconPreview');
+ const selectedIcon = document.getElementById('selectedIcon');
- if (fileInput) fileInput.value = '';
- if (fileNameDisplay) fileNameDisplay.textContent = 'Nebyl vybrán žádný soubor';
- if (previewImg) {
- previewImg.src = '';
- previewImg.classList.add('hidden');
+ if (customIconInput && customIconPreview && selectedIcon) {
+ customIconInput.value = '';
+ customIconPreview.src = '';
+ customIconPreview.classList.add('hidden');
+ selectedIcon.classList.remove('hidden');
+ }
+
+ // Reset color picker to default
+ const colorInput = document.getElementById('appColor');
+ if (colorInput) {
+ colorInput.value = '#4a6cf7';
}
// Show the modal
- document.getElementById('appModal').classList.remove('hidden');
+ const modal = document.getElementById('appModal');
+ if (modal) {
+ modal.classList.remove('hidden');
+ }
}
function closeAppModal() {
@@ -1898,37 +1903,64 @@ function closeAppModal() {
// Handle file input change and preview
function setupFileInput() {
- const fileInput = document.getElementById('appIcon');
- const fileNameDisplay = document.getElementById('fileName');
- const previewImg = document.getElementById('appIconPreview');
+ const fileInput = document.getElementById('customIconInput');
+ const previewImg = document.getElementById('customIconPreview');
+ const selectedIcon = document.getElementById('selectedIcon');
- if (!fileInput || !fileNameDisplay || !previewImg) return;
+ if (!fileInput || !previewImg || !selectedIcon) return;
- fileInput.addEventListener('change', (e) => {
- const file = e.target.files[0];
-
+ fileInput.addEventListener('change', function() {
+ const file = this.files[0];
if (file) {
- // Update file name display
- fileNameDisplay.textContent = file.name;
-
- // Show preview if it's an image
+ // Check if the file is an image
if (file.type.startsWith('image/')) {
const reader = new FileReader();
- reader.onload = (e) => {
+ reader.onload = function(e) {
previewImg.src = e.target.result;
previewImg.classList.remove('hidden');
- };
+ selectedIcon.classList.add('hidden');
+
+ // Set the appIcon value to 'custom' to indicate a custom icon is being used
+ document.getElementById('appIcon').value = 'custom';
+ }
reader.readAsDataURL(file);
} else {
previewImg.classList.add('hidden');
+ selectedIcon.classList.remove('hidden');
+ showNotification('Vyberte prosím obrázek (JPG, PNG, GIF, SVG)', 'warning');
+ this.value = ''; // Reset the file input
}
} else {
- fileNameDisplay.textContent = 'Nebyl vybrán žádný soubor';
previewImg.classList.add('hidden');
+ selectedIcon.classList.remove('hidden');
+ document.getElementById('appIcon').value = '';
}
});
}
+document.addEventListener('DOMContentLoaded', function() {
+ const appColor = document.getElementById('appColor');
+ const appColorText = document.getElementById('appColorText');
+
+ if (appColor && appColorText) {
+ // Update text input when color picker changes
+ appColor.addEventListener('input', function() {
+ appColorText.value = this.value.toUpperCase();
+ });
+
+ // Update color picker when text input changes
+ appColorText.addEventListener('input', function() {
+ // Validate hex color
+ const colorRegex = /^#?([a-f0-9]{6}|[a-f0-9]{3})$/i;
+ if (colorRegex.test(this.value)) {
+ // Ensure the # prefix is present
+ const hexColor = this.value.startsWith('#') ? this.value : `#${this.value}`;
+ appColor.value = hexColor;
+ }
+ });
+ }
+});
+
// Reset form when modal is closed
document.getElementById('appModal').addEventListener('hidden.bs.modal', function () {
const form = document.getElementById('appForm');
@@ -2035,10 +2067,8 @@ document.getElementById('logoutBtn').addEventListener('click', function() {
window.location.href = '/';
});
-// DOM Elements
-let bannerText, bannerVisible, bannerBgColor, bannerTextColor, bannerTextAlign, bannerFontSize,
- bannerPadding, bannerMargin, bannerBorderRadius, bannerPreview, bannerPreviewContent,
- bannerPreviewText, bannerPreviewBg, bgColorPreview, textColorPreview, saveBannerBtn,
+// DOM Elements - these will be initialized in DOMContentLoaded
+let bannerPreviewContent, bannerPreviewText, bannerPreviewBg, bgColorPreview, textColorPreview, saveBannerBtn,
stylePresets, currentImage = null, currentTemplate = 'modern-minimal';
// Preset styles