kundenkarte/js/kundenkarte.js

1056 lines
45 KiB
JavaScript
Executable file

/**
* KundenKarte Module JavaScript
* Copyright (C) 2026 Alles Watt lauft
*/
(function() {
'use strict';
// Namespace
window.KundenKarte = window.KundenKarte || {};
// Get base URL for AJAX calls
var baseUrl = (typeof DOL_URL_ROOT !== 'undefined') ? DOL_URL_ROOT : '';
if (!baseUrl) {
// Try to detect from script src
var scripts = document.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
var src = scripts[i].src;
if (src && src.indexOf('/kundenkarte/js/kundenkarte.js') > -1) {
baseUrl = src.replace('/custom/kundenkarte/js/kundenkarte.js', '').replace(/\?.*$/, '');
break;
}
}
}
/**
* Tree Component
*/
KundenKarte.Tree = {
tooltipTimeout: null,
hideTimeout: null,
currentTooltip: null,
currentItem: null,
init: function() {
this.bindEvents();
},
bindEvents: function() {
var self = this;
// Toggle tree nodes - MUST use stopImmediatePropagation for delegated handlers on same element
$(document).on('click', '.kundenkarte-tree-toggle', function(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
var $toggle = $(this);
var $node = $toggle.closest('.kundenkarte-tree-node');
var $children = $node.children('.kundenkarte-tree-children');
$toggle.toggleClass('collapsed');
$children.toggleClass('collapsed');
});
// Expand all nodes
$(document).on('click', '#btn-expand-all', function(e) {
e.preventDefault();
self.expandAll();
});
// Collapse all nodes
$(document).on('click', '#btn-collapse-all', function(e) {
e.preventDefault();
self.collapseAll();
});
// Hover tooltip on ICON only - show after delay
$(document).on('mouseenter', '.kundenkarte-tooltip-trigger', function(e) {
var $trigger = $(this);
var anlageId = $trigger.data('anlage-id');
if (!anlageId) return;
// Cancel any pending hide
clearTimeout(self.hideTimeout);
self.hideTimeout = null;
self.currentItem = $trigger;
self.tooltipTimeout = setTimeout(function() {
self.showTooltip($trigger, anlageId);
}, 300);
});
// Hide tooltip when leaving icon
$(document).on('mouseleave', '.kundenkarte-tooltip-trigger', function() {
clearTimeout(self.tooltipTimeout);
self.tooltipTimeout = null;
self.currentItem = null;
// Hide after short delay (allows moving to tooltip)
self.hideTimeout = setTimeout(function() {
self.hideTooltip();
}, 100);
});
// Images tooltip on hover
$(document).on('mouseenter', '.kundenkarte-images-trigger', function(e) {
var $trigger = $(this);
var anlageId = $trigger.data('anlage-id');
if (!anlageId) return;
clearTimeout(self.hideTimeout);
self.hideTimeout = null;
self.tooltipTimeout = setTimeout(function() {
self.showImagesPopup($trigger, anlageId);
}, 300);
});
$(document).on('mouseleave', '.kundenkarte-images-trigger', function() {
clearTimeout(self.tooltipTimeout);
self.tooltipTimeout = null;
self.hideTimeout = setTimeout(function() {
self.hideTooltip();
}, 100);
});
// Documents tooltip on hover
$(document).on('mouseenter', '.kundenkarte-docs-trigger', function(e) {
var $trigger = $(this);
var anlageId = $trigger.data('anlage-id');
if (!anlageId) return;
clearTimeout(self.hideTimeout);
self.hideTimeout = null;
self.tooltipTimeout = setTimeout(function() {
self.showDocsPopup($trigger, anlageId);
}, 300);
});
$(document).on('mouseleave', '.kundenkarte-docs-trigger', function() {
clearTimeout(self.tooltipTimeout);
self.tooltipTimeout = null;
self.hideTimeout = setTimeout(function() {
self.hideTooltip();
}, 100);
});
// Keep tooltip visible when hovering over it
$(document).on('mouseenter', '#kundenkarte-tooltip', function() {
clearTimeout(self.hideTimeout);
self.hideTimeout = null;
});
// Hide when leaving tooltip
$(document).on('mouseleave', '#kundenkarte-tooltip', function() {
self.hideTooltip();
});
// Select item
$(document).on('click', '.kundenkarte-tree-item', function(e) {
if ($(e.target).closest('.kundenkarte-tree-toggle, .kundenkarte-tree-actions, .kundenkarte-tree-files').length) {
return;
}
$('.kundenkarte-tree-item').removeClass('selected');
$(this).addClass('selected');
var anlageId = $(this).data('anlage-id');
if (anlageId) {
$(document).trigger('kundenkarte:element:selected', [anlageId]);
}
});
},
showTooltip: function($item, anlageId) {
var self = this;
// Get tooltip data from data attribute (faster than AJAX)
var tooltipDataStr = $item.attr('data-tooltip');
if (!tooltipDataStr) {
console.log('No tooltip data for anlage', anlageId);
return;
}
var data;
try {
data = JSON.parse(tooltipDataStr);
} catch(e) {
console.error('Failed to parse tooltip JSON:', e, tooltipDataStr);
return;
}
var html = self.buildTooltipHtml(data);
var $tooltip = $('#kundenkarte-tooltip');
if (!$tooltip.length) {
$tooltip = $('<div id="kundenkarte-tooltip" class="kundenkarte-tooltip"></div>');
$('body').append($tooltip);
}
$tooltip.html(html);
// Position tooltip
var offset = $item.offset();
var itemWidth = $item.outerWidth();
var windowWidth = $(window).width();
var scrollTop = $(window).scrollTop();
// First show to calculate width
$tooltip.css({ visibility: 'hidden', display: 'block' });
var tooltipWidth = $tooltip.outerWidth();
var tooltipHeight = $tooltip.outerHeight();
$tooltip.css({ visibility: '', display: '' });
var left = offset.left + itemWidth + 10;
if (left + tooltipWidth > windowWidth - 20) {
left = offset.left - tooltipWidth - 10;
}
if (left < 10) {
left = 10;
}
var top = offset.top;
// Prevent tooltip from going below viewport
if (top + tooltipHeight > scrollTop + $(window).height() - 20) {
top = scrollTop + $(window).height() - tooltipHeight - 20;
}
$tooltip.css({
top: top,
left: left
}).addClass('visible').show();
self.currentTooltip = $tooltip;
},
hideTooltip: function() {
clearTimeout(this.hideTimeout);
this.hideTimeout = null;
var $tooltip = $('#kundenkarte-tooltip');
if ($tooltip.length) {
$tooltip.removeClass('visible').hide();
}
this.currentTooltip = null;
},
showImagesPopup: function($trigger, anlageId) {
var self = this;
// Load images via AJAX - use absolute path
$.ajax({
url: baseUrl + '/custom/kundenkarte/ajax/anlage_images.php',
data: { anlage_id: anlageId },
dataType: 'json',
success: function(response) {
if (!response.images || response.images.length === 0) {
return;
}
var html = '<div class="kundenkarte-images-popup">';
html += '<div class="kundenkarte-images-grid">';
for (var i = 0; i < response.images.length; i++) {
var img = response.images[i];
html += '<a href="' + img.url + '" target="_blank" class="kundenkarte-images-thumb">';
html += '<img src="' + img.thumb + '" alt="' + self.escapeHtml(img.name) + '">';
html += '</a>';
}
html += '</div>';
html += '</div>';
var $tooltip = $('#kundenkarte-tooltip');
if (!$tooltip.length) {
$tooltip = $('<div id="kundenkarte-tooltip" class="kundenkarte-tooltip"></div>');
$('body').append($tooltip);
}
$tooltip.html(html);
// Position tooltip
var offset = $trigger.offset();
var windowWidth = $(window).width();
var scrollTop = $(window).scrollTop();
$tooltip.css({ visibility: 'hidden', display: 'block' });
var tooltipWidth = $tooltip.outerWidth();
var tooltipHeight = $tooltip.outerHeight();
$tooltip.css({ visibility: '', display: '' });
var left = offset.left + $trigger.outerWidth() + 10;
if (left + tooltipWidth > windowWidth - 20) {
left = offset.left - tooltipWidth - 10;
}
if (left < 10) left = 10;
var top = offset.top;
if (top + tooltipHeight > scrollTop + $(window).height() - 20) {
top = scrollTop + $(window).height() - tooltipHeight - 20;
}
$tooltip.css({ top: top, left: left }).addClass('visible').show();
self.currentTooltip = $tooltip;
}
});
},
showDocsPopup: function($trigger, anlageId) {
var self = this;
// Load documents via AJAX
$.ajax({
url: baseUrl + '/custom/kundenkarte/ajax/anlage_docs.php',
data: { anlage_id: anlageId },
dataType: 'json',
success: function(response) {
if (!response.docs || response.docs.length === 0) {
return;
}
// Visual document cards with icons
var html = '<div class="kundenkarte-docs-popup">';
html += '<div class="kundenkarte-docs-grid">';
for (var i = 0; i < response.docs.length; i++) {
var doc = response.docs[i];
var iconClass = doc.type === 'pdf' ? 'fa-file-pdf-o' : 'fa-file-text-o';
var iconColor = doc.type === 'pdf' ? '#e74c3c' : '#f39c12';
html += '<a href="' + doc.url + '" target="_blank" class="kundenkarte-docs-card">';
html += '<div class="kundenkarte-docs-card-icon" style="color:' + iconColor + '">';
html += '<i class="fa ' + iconClass + '"></i>';
html += '</div>';
html += '<div class="kundenkarte-docs-card-name">' + self.escapeHtml(doc.name) + '</div>';
html += '</a>';
}
html += '</div>';
html += '</div>';
var $tooltip = $('#kundenkarte-tooltip');
if (!$tooltip.length) {
$tooltip = $('<div id="kundenkarte-tooltip" class="kundenkarte-tooltip"></div>');
$('body').append($tooltip);
}
$tooltip.html(html);
// Position tooltip
var offset = $trigger.offset();
var windowWidth = $(window).width();
var scrollTop = $(window).scrollTop();
$tooltip.css({ visibility: 'hidden', display: 'block' });
var tooltipWidth = $tooltip.outerWidth();
var tooltipHeight = $tooltip.outerHeight();
$tooltip.css({ visibility: '', display: '' });
var left = offset.left + $trigger.outerWidth() + 10;
if (left + tooltipWidth > windowWidth - 20) {
left = offset.left - tooltipWidth - 10;
}
if (left < 10) left = 10;
var top = offset.top;
if (top + tooltipHeight > scrollTop + $(window).height() - 20) {
top = scrollTop + $(window).height() - tooltipHeight - 20;
}
$tooltip.css({ top: top, left: left }).addClass('visible').show();
self.currentTooltip = $tooltip;
}
});
},
buildTooltipHtml: function(data) {
var html = '<div class="kundenkarte-tooltip-header">';
html += '<span class="kundenkarte-tooltip-icon"><i class="fa ' + (data.picto || 'fa-cube') + '"></i></span>';
html += '<div>';
html += '<div class="kundenkarte-tooltip-title">' + this.escapeHtml(data.label || '') + '</div>';
html += '<div class="kundenkarte-tooltip-type">' + this.escapeHtml(data.type || data.type_label || '') + '</div>';
html += '</div></div>';
html += '<div class="kundenkarte-tooltip-fields">';
// Dynamic fields only (from PHP data-tooltip attribute)
if (data.fields) {
for (var key in data.fields) {
if (data.fields.hasOwnProperty(key)) {
var field = data.fields[key];
// Handle header fields as section titles
if (field.type === 'header') {
html += '<span class="kundenkarte-tooltip-field-header" style="display:block;width:100%;font-weight:bold;margin-top:8px;padding-top:8px;border-top:1px solid #ddd;">' + this.escapeHtml(field.label) + '</span>';
} else if (field.value) {
html += '<span class="kundenkarte-tooltip-field-label">' + this.escapeHtml(field.label) + ':</span>';
html += '<span class="kundenkarte-tooltip-field-value">' + this.escapeHtml(field.value) + '</span>';
}
}
}
}
html += '</div>';
// Notes (note_html is already sanitized and formatted with <br> by PHP)
if (data.note_html) {
html += '<div class="kundenkarte-tooltip-note" style="margin-top:10px;padding-top:10px;border-top:1px solid #eee;font-size:0.9em;color:#666;">';
html += '<i class="fa fa-sticky-note"></i><br>' + data.note_html;
html += '</div>';
}
// Images (from AJAX)
if (data.images && data.images.length > 0) {
html += '<div class="kundenkarte-tooltip-images">';
for (var i = 0; i < Math.min(data.images.length, 4); i++) {
html += '<img src="' + data.images[i].thumb_url + '" class="kundenkarte-tooltip-thumb" alt="">';
}
html += '</div>';
}
return html;
},
escapeHtml: function(text) {
if (!text) return '';
var div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
},
escapeHtmlPreservingBreaks: function(text) {
if (!text) return '';
// Convert <br> to newlines first (for old data with <br> tags)
text = text.replace(/<br\s*\/?>/gi, '\n');
return this.escapeHtml(text);
},
refresh: function(socId, systemId) {
var $container = $('.kundenkarte-tree[data-system="' + systemId + '"]');
if (!$container.length) return;
$.ajax({
url: baseUrl + '/custom/kundenkarte/ajax/anlage_tree.php',
data: { socid: socId, system: systemId },
success: function(html) {
$container.html(html);
}
});
},
expandAll: function() {
$('.kundenkarte-tree-toggle').removeClass('collapsed');
$('.kundenkarte-tree-children').removeClass('collapsed');
},
collapseAll: function() {
$('.kundenkarte-tree-toggle').addClass('collapsed');
$('.kundenkarte-tree-children').addClass('collapsed');
}
};
/**
* Favorite Products Component
*/
KundenKarte.Favorites = {
init: function() {
this.bindEvents();
},
bindEvents: function() {
// Select all checkbox
$(document).on('change', '#kundenkarte-select-all', function() {
var checked = $(this).prop('checked');
$('.kundenkarte-favorites-table input[type="checkbox"][name="selected_products[]"]').prop('checked', checked);
KundenKarte.Favorites.updateGenerateButton();
});
// Individual checkbox
$(document).on('change', '.kundenkarte-favorites-table input[type="checkbox"][name="selected_products[]"]', function() {
KundenKarte.Favorites.updateGenerateButton();
});
// Save button click
$(document).on('click', '.kundenkarte-qty-save', function(e) {
e.preventDefault();
var $btn = $(this);
var favId = $btn.data('fav-id');
var $input = $('input.kundenkarte-favorites-qty[data-fav-id="' + favId + '"]');
var qtyStr = $input.val().replace(',', '.');
var qty = parseFloat(qtyStr);
if (!isNaN(qty) && qty > 0) {
// Limit to 2 decimal places
qty = Math.round(qty * 100) / 100;
// Format nicely
var display = (qty % 1 === 0) ? qty.toString() : qty.toFixed(2).replace(/\.?0+$/, '');
$input.val(display);
// Visual feedback
$btn.prop('disabled', true);
$btn.find('i').removeClass('fa-save').addClass('fa-spinner fa-spin');
$.ajax({
url: baseUrl + '/custom/kundenkarte/ajax/favorite_update.php',
method: 'POST',
data: { id: favId, qty: qty, token: $('input[name="token"]').val() },
success: function() {
$btn.find('i').removeClass('fa-spinner fa-spin').addClass('fa-check').css('color', '#0a0');
setTimeout(function() {
$btn.find('i').removeClass('fa-check').addClass('fa-save').css('color', '');
$btn.prop('disabled', false);
}, 1500);
},
error: function() {
$btn.find('i').removeClass('fa-spinner fa-spin').addClass('fa-exclamation-triangle').css('color', '#c00');
setTimeout(function() {
$btn.find('i').removeClass('fa-exclamation-triangle').addClass('fa-save').css('color', '');
$btn.prop('disabled', false);
}, 2000);
}
});
}
});
},
updateGenerateButton: function() {
var count = $('.kundenkarte-favorites-table input[type="checkbox"][name="selected_products[]"]:checked').length;
var $btn = $('#btn-generate-order');
if (count > 0) {
$btn.prop('disabled', false).text($btn.data('text').replace('%d', count));
} else {
$btn.prop('disabled', true).text($btn.data('text-none'));
}
}
};
/**
* System Tabs Component
*/
KundenKarte.SystemTabs = {
init: function() {
this.bindEvents();
},
bindEvents: function() {
$(document).on('click', '.kundenkarte-system-tab:not(.active):not(.kundenkarte-system-tab-add)', function() {
var systemId = $(this).data('system');
KundenKarte.SystemTabs.switchTo(systemId);
});
},
switchTo: function(systemId) {
$('.kundenkarte-system-tab').removeClass('active');
$('.kundenkarte-system-tab[data-system="' + systemId + '"]').addClass('active');
$('.kundenkarte-system-content').hide();
$('.kundenkarte-system-content[data-system="' + systemId + '"]').show();
// Update URL without reload
var url = new URL(window.location.href);
url.searchParams.set('system', systemId);
window.history.replaceState({}, '', url);
}
};
/**
* Icon Picker Component with Custom Icon Upload
*/
KundenKarte.IconPicker = {
// Common FontAwesome icons for installations/technical use
icons: [
// Electrical
'fa-bolt', 'fa-plug', 'fa-power-off', 'fa-charging-station', 'fa-battery-full', 'fa-battery-half',
'fa-car-battery', 'fa-solar-panel', 'fa-sun', 'fa-lightbulb', 'fa-toggle-on', 'fa-toggle-off',
// Network/Internet
'fa-wifi', 'fa-network-wired', 'fa-server', 'fa-database', 'fa-hdd', 'fa-ethernet',
'fa-broadcast-tower', 'fa-satellite-dish', 'fa-satellite', 'fa-signal', 'fa-rss',
// TV/Media
'fa-tv', 'fa-play-circle', 'fa-video', 'fa-film', 'fa-podcast', 'fa-music',
// Temperature/Climate
'fa-thermometer-half', 'fa-temperature-high', 'fa-temperature-low', 'fa-fire', 'fa-fire-alt',
'fa-snowflake', 'fa-wind', 'fa-fan', 'fa-air-freshener',
// Building/Structure
'fa-home', 'fa-building', 'fa-warehouse', 'fa-door-open', 'fa-door-closed', 'fa-archway',
// Devices/Hardware
'fa-microchip', 'fa-memory', 'fa-sim-card', 'fa-sd-card', 'fa-usb', 'fa-desktop', 'fa-laptop',
'fa-mobile-alt', 'fa-tablet-alt', 'fa-keyboard', 'fa-print', 'fa-fax',
// Security
'fa-shield-alt', 'fa-lock', 'fa-unlock', 'fa-key', 'fa-fingerprint', 'fa-eye', 'fa-video',
'fa-bell', 'fa-exclamation-triangle', 'fa-user-shield',
// Objects
'fa-cube', 'fa-cubes', 'fa-box', 'fa-boxes', 'fa-archive', 'fa-toolbox', 'fa-tools', 'fa-wrench',
'fa-cog', 'fa-cogs', 'fa-sliders-h',
// Layout
'fa-th', 'fa-th-large', 'fa-th-list', 'fa-grip-horizontal', 'fa-grip-vertical', 'fa-bars',
'fa-stream', 'fa-layer-group', 'fa-project-diagram', 'fa-share-alt', 'fa-sitemap',
// Arrows/Direction
'fa-exchange-alt', 'fa-arrows-alt', 'fa-expand', 'fa-compress', 'fa-random',
// Misc
'fa-tachometer-alt', 'fa-chart-line', 'fa-chart-bar', 'fa-chart-pie', 'fa-chart-area',
'fa-clock', 'fa-calendar', 'fa-tag', 'fa-tags', 'fa-bookmark', 'fa-star', 'fa-heart',
'fa-check', 'fa-times', 'fa-plus', 'fa-minus', 'fa-info-circle', 'fa-question-circle',
'fa-dot-circle', 'fa-circle', 'fa-square', 'fa-adjust'
],
customIcons: [],
currentInput: null,
currentTab: 'fontawesome',
init: function() {
this.createModal();
this.bindEvents();
},
createModal: function() {
if ($('#kundenkarte-icon-picker-modal').length) return;
var self = this;
var html = '<div id="kundenkarte-icon-picker-modal" class="kundenkarte-modal">';
html += '<div class="kundenkarte-modal-content" style="max-width:700px;">';
html += '<div class="kundenkarte-modal-header">';
html += '<h3>Icon auswählen</h3>';
html += '<span class="kundenkarte-modal-close">&times;</span>';
html += '</div>';
html += '<div class="kundenkarte-modal-body">';
// Tabs
html += '<div class="kundenkarte-icon-tabs" style="display:flex;gap:5px;margin-bottom:15px;border-bottom:2px solid #e0e0e0;padding-bottom:10px;">';
html += '<button type="button" class="button kundenkarte-icon-tab active" data-tab="fontawesome"><i class="fa fa-font-awesome"></i> Font Awesome</button>';
html += '<button type="button" class="button kundenkarte-icon-tab" data-tab="custom"><i class="fa fa-upload"></i> Eigene Icons</button>';
html += '</div>';
// Font Awesome Tab Content
html += '<div class="kundenkarte-icon-tab-content" data-tab="fontawesome">';
html += '<input type="text" id="kundenkarte-icon-search" class="flat" placeholder="Suchen..." style="width:100%;margin-bottom:10px;">';
html += '<div class="kundenkarte-icon-grid" id="kundenkarte-fa-icons">';
for (var i = 0; i < this.icons.length; i++) {
html += '<div class="kundenkarte-icon-item" data-icon="' + this.icons[i] + '" data-type="fa" title="' + this.icons[i] + '">';
html += '<i class="fa ' + this.icons[i] + '"></i>';
html += '</div>';
}
html += '</div>';
html += '</div>';
// Custom Icons Tab Content
html += '<div class="kundenkarte-icon-tab-content" data-tab="custom" style="display:none;">';
// Upload area
html += '<div class="kundenkarte-icon-upload-area" style="border:2px dashed #ccc;border-radius:8px;padding:20px;text-align:center;margin-bottom:15px;background:#f9f9f9;">';
html += '<i class="fa fa-cloud-upload" style="font-size:32px;color:#888;margin-bottom:10px;display:block;"></i>';
html += '<p style="margin:0 0 10px 0;">Icon hochladen (PNG, JPG, SVG, max 500KB)</p>';
html += '<input type="file" id="kundenkarte-icon-upload" accept=".png,.jpg,.jpeg,.gif,.svg,.webp" style="display:none;">';
html += '<button type="button" class="button" id="kundenkarte-icon-upload-btn"><i class="fa fa-plus"></i> Datei auswählen</button>';
html += '</div>';
// Custom icons grid
html += '<div class="kundenkarte-icon-grid" id="kundenkarte-custom-icons">';
html += '<p class="opacitymedium" style="grid-column:1/-1;text-align:center;">Lade eigene Icons...</p>';
html += '</div>';
html += '</div>';
html += '</div>';
html += '</div>';
html += '</div>';
$('body').append(html);
},
bindEvents: function() {
var self = this;
// Open picker
$(document).on('click', '.kundenkarte-icon-picker-btn', function(e) {
e.preventDefault();
var inputName = $(this).data('input');
self.currentInput = $('input[name="' + inputName + '"]');
self.open();
});
// Close modal
$(document).on('click', '.kundenkarte-modal-close, #kundenkarte-icon-picker-modal', function(e) {
if (e.target === this || $(e.target).hasClass('kundenkarte-modal-close')) {
self.close();
}
});
// Tab switching
$(document).on('click', '.kundenkarte-icon-tab', function() {
var tab = $(this).data('tab');
self.switchTab(tab);
});
// Select FA icon
$(document).on('click', '.kundenkarte-icon-item[data-type="fa"]', function() {
var icon = $(this).data('icon');
self.selectIcon(icon, 'fa');
});
// Select custom icon
$(document).on('click', '.kundenkarte-icon-item[data-type="custom"]', function() {
var iconUrl = $(this).data('icon');
self.selectIcon(iconUrl, 'custom');
});
// Search filter (FA only)
$(document).on('input', '#kundenkarte-icon-search', function() {
var search = $(this).val().toLowerCase();
$('#kundenkarte-fa-icons .kundenkarte-icon-item').each(function() {
var icon = $(this).data('icon').toLowerCase();
$(this).toggle(icon.indexOf(search) > -1);
});
});
// Upload button click
$(document).on('click', '#kundenkarte-icon-upload-btn', function() {
$('#kundenkarte-icon-upload').click();
});
// File selected
$(document).on('change', '#kundenkarte-icon-upload', function() {
var file = this.files[0];
if (file) {
self.uploadIcon(file);
}
});
// Delete custom icon
$(document).on('click', '.kundenkarte-icon-delete', function(e) {
e.stopPropagation();
var filename = $(this).data('filename');
self.showDeleteConfirm(filename);
});
// ESC key to close
$(document).on('keydown', function(e) {
if (e.key === 'Escape') {
self.close();
}
});
},
switchTab: function(tab) {
this.currentTab = tab;
$('.kundenkarte-icon-tab').removeClass('active');
$('.kundenkarte-icon-tab[data-tab="' + tab + '"]').addClass('active');
$('.kundenkarte-icon-tab-content').hide();
$('.kundenkarte-icon-tab-content[data-tab="' + tab + '"]').show();
if (tab === 'custom') {
this.loadCustomIcons();
}
},
loadCustomIcons: function() {
var self = this;
var $grid = $('#kundenkarte-custom-icons');
$.ajax({
url: baseUrl + '/custom/kundenkarte/ajax/icon_upload.php',
data: { action: 'list' },
dataType: 'json',
success: function(response) {
$grid.empty();
if (response.icons && response.icons.length > 0) {
self.customIcons = response.icons;
for (var i = 0; i < response.icons.length; i++) {
var icon = response.icons[i];
var html = '<div class="kundenkarte-icon-item kundenkarte-custom-icon-item" data-icon="' + icon.url + '" data-type="custom" title="' + icon.name + '" style="position:relative;">';
html += '<img src="' + icon.url + '" alt="' + icon.name + '" style="max-width:32px;max-height:32px;">';
html += '<span class="kundenkarte-icon-delete" data-filename="' + icon.filename + '" title="Löschen" style="position:absolute;top:-5px;right:-5px;background:#c00;color:#fff;border-radius:50%;width:16px;height:16px;font-size:10px;line-height:16px;text-align:center;cursor:pointer;display:none;">&times;</span>';
html += '</div>';
$grid.append(html);
}
} else {
$grid.html('<p class="opacitymedium" style="grid-column:1/-1;text-align:center;">Noch keine eigenen Icons hochgeladen.</p>');
}
},
error: function() {
$grid.html('<p class="warning" style="grid-column:1/-1;text-align:center;">Fehler beim Laden der Icons.</p>');
}
});
},
uploadIcon: function(file) {
var self = this;
var formData = new FormData();
formData.append('action', 'upload');
formData.append('icon', file);
$.ajax({
url: baseUrl + '/custom/kundenkarte/ajax/icon_upload.php',
method: 'POST',
data: formData,
processData: false,
contentType: false,
dataType: 'json',
success: function(response) {
if (response.success) {
self.loadCustomIcons();
// Auto-select uploaded icon
setTimeout(function() {
self.selectIcon(response.icon.url, 'custom');
}, 500);
} else {
alert('Fehler: ' + (response.error || 'Unbekannter Fehler'));
}
},
error: function(xhr) {
var msg = 'Upload fehlgeschlagen';
try {
var resp = JSON.parse(xhr.responseText);
msg = resp.error || msg;
} catch(e) {}
alert(msg);
}
});
// Reset input
$('#kundenkarte-icon-upload').val('');
},
deleteIcon: function(filename) {
var self = this;
$.ajax({
url: baseUrl + '/custom/kundenkarte/ajax/icon_upload.php',
method: 'POST',
data: { action: 'delete', filename: filename },
dataType: 'json',
success: function(response) {
if (response.success) {
self.loadCustomIcons();
} else {
alert('Fehler: ' + (response.error || 'Löschen fehlgeschlagen'));
}
}
});
},
showDeleteConfirm: function(filename) {
var self = this;
// Remove any existing confirm dialog
$('#kundenkarte-delete-confirm').remove();
// Create Dolibarr-style confirmation dialog
var html = '<div id="kundenkarte-delete-confirm" class="kundenkarte-confirm-overlay" style="position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:10001;display:flex;align-items:center;justify-content:center;">';
html += '<div class="kundenkarte-confirm-box" style="background:#fff;border-radius:6px;box-shadow:0 4px 20px rgba(0,0,0,0.3);max-width:400px;width:90%;">';
// Header (Dolibarr style)
html += '<div class="kundenkarte-confirm-header" style="background:linear-gradient(to bottom,#f8f8f8,#e8e8e8);border-bottom:1px solid #ccc;padding:12px 15px;border-radius:6px 6px 0 0;">';
html += '<span style="font-weight:bold;font-size:14px;"><i class="fa fa-exclamation-triangle" style="color:#f0ad4e;margin-right:8px;"></i>Bestätigung</span>';
html += '</div>';
// Body
html += '<div class="kundenkarte-confirm-body" style="padding:20px;text-align:center;">';
html += '<p style="margin:0 0 5px 0;font-size:14px;">Möchten Sie dieses Icon wirklich löschen?</p>';
html += '<p style="margin:0;color:#666;font-size:12px;"><code>' + this.escapeHtml(filename) + '</code></p>';
html += '</div>';
// Footer with buttons (Dolibarr style)
html += '<div class="kundenkarte-confirm-footer" style="background:#f5f5f5;border-top:1px solid #ddd;padding:12px 15px;text-align:center;border-radius:0 0 6px 6px;">';
html += '<button type="button" class="button" id="kundenkarte-confirm-yes" style="background:#c9302c;color:#fff;border:1px solid #ac2925;padding:6px 20px;margin-right:10px;cursor:pointer;"><i class="fa fa-check"></i> Ja, löschen</button>';
html += '<button type="button" class="button" id="kundenkarte-confirm-no" style="padding:6px 20px;cursor:pointer;"><i class="fa fa-times"></i> Abbrechen</button>';
html += '</div>';
html += '</div>';
html += '</div>';
$('body').append(html);
// Bind events
$('#kundenkarte-confirm-yes').on('click', function() {
$('#kundenkarte-delete-confirm').remove();
self.deleteIcon(filename);
});
$('#kundenkarte-confirm-no, #kundenkarte-delete-confirm').on('click', function(e) {
if (e.target === this) {
$('#kundenkarte-delete-confirm').remove();
}
});
// ESC to close
$(document).one('keydown.confirmDialog', function(e) {
if (e.key === 'Escape') {
$('#kundenkarte-delete-confirm').remove();
}
});
},
escapeHtml: function(text) {
if (!text) return '';
var div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
},
selectIcon: function(icon, type) {
if (this.currentInput) {
// For custom icons, store the URL with a prefix to distinguish
var value = (type === 'custom') ? 'img:' + icon : icon;
this.currentInput.val(value);
// Update preview
var $wrapper = this.currentInput.closest('.kundenkarte-icon-picker-wrapper');
var $preview = $wrapper.find('.kundenkarte-icon-preview');
if ($preview.length) {
if (type === 'custom') {
$preview.html('<img src="' + icon + '" style="max-width:20px;max-height:20px;">');
} else {
$preview.html('<i class="fa ' + icon + '"></i>');
}
}
}
this.close();
},
open: function() {
$('#kundenkarte-icon-search').val('');
$('#kundenkarte-fa-icons .kundenkarte-icon-item').show();
this.switchTab('fontawesome');
$('#kundenkarte-icon-picker-modal').addClass('visible');
},
close: function() {
$('#kundenkarte-icon-picker-modal').removeClass('visible');
this.currentInput = null;
}
};
/**
* Dynamic Fields Component
* Loads and renders type-specific fields when creating/editing anlagen
*/
KundenKarte.DynamicFields = {
init: function() {
var self = this;
var $typeSelect = $('select[name="fk_anlage_type"]');
var $container = $('#dynamic_fields');
if (!$typeSelect.length || !$container.length) return;
// Load fields when type changes
$typeSelect.on('change', function() {
self.loadFields($(this).val());
});
// Load initial fields if type is already selected
if ($typeSelect.val()) {
self.loadFields($typeSelect.val());
}
},
loadFields: function(typeId) {
var $container = $('#dynamic_fields');
if (!typeId) {
$container.html('');
return;
}
// Get anlage_id if editing or copying
var anlageId = $('input[name="anlage_id"]').val() || $('input[name="copy_from"]').val() || 0;
$.ajax({
url: baseUrl + '/custom/kundenkarte/ajax/type_fields.php',
data: { type_id: typeId, anlage_id: anlageId },
dataType: 'json',
success: function(data) {
if (data.fields && data.fields.length > 0) {
var html = '';
data.fields.forEach(function(field) {
if (field.type === 'header') {
// Header row spans both columns with styling
html += '<tr class="liste_titre dynamic-field-row"><th colspan="2" style="background:#f0f0f0;padding:8px;">' + KundenKarte.DynamicFields.escapeHtml(field.label) + '</th></tr>';
} else {
html += '<tr class="dynamic-field-row"><td class="titlefield">' + KundenKarte.DynamicFields.escapeHtml(field.label);
if (field.required) html += ' <span class="fieldrequired">*</span>';
html += '</td><td>';
html += KundenKarte.DynamicFields.renderField(field);
html += '</td></tr>';
}
});
$container.html(html);
} else {
$container.html('');
}
}
});
},
renderField: function(field) {
var name = 'field_' + field.code;
var value = field.value || '';
var required = field.required ? ' required' : '';
switch (field.type) {
case 'text':
return '<input type="text" name="' + name + '" class="flat minwidth300" value="' + this.escapeHtml(value) + '"' + required + '>';
case 'textarea':
return '<textarea name="' + name + '" class="flat minwidth300" rows="3"' + required + '>' + this.escapeHtml(value) + '</textarea>';
case 'number':
var attrs = '';
if (field.options) {
var opts = field.options.split('|');
opts.forEach(function(opt) {
var parts = opt.split(':');
if (parts.length === 2) {
attrs += ' ' + parts[0] + '="' + parts[1] + '"';
}
});
}
return '<input type="number" name="' + name + '" class="flat" value="' + this.escapeHtml(value) + '"' + attrs + required + '>';
case 'select':
var html = '<select name="' + name + '" class="flat minwidth200"' + required + '>';
html += '<option value="">-- Auswählen --</option>';
if (field.options) {
var options = field.options.split('|');
options.forEach(function(opt) {
var selected = (opt === value) ? ' selected' : '';
html += '<option value="' + KundenKarte.DynamicFields.escapeHtml(opt) + '"' + selected + '>' + KundenKarte.DynamicFields.escapeHtml(opt) + '</option>';
});
}
html += '</select>';
return html;
case 'date':
var inputId = 'date_' + name.replace(/[^a-zA-Z0-9]/g, '_');
return '<input type="date" name="' + name + '" id="' + inputId + '" class="flat" value="' + this.escapeHtml(value) + '"' + required + '>' +
'<button type="button" class="button buttongen" style="margin-left:4px;padding:2px 6px;font-size:11px;" onclick="document.getElementById(\'' + inputId + '\').value = new Date().toISOString().split(\'T\')[0];" title="Heute">Heute</button>';
case 'checkbox':
var checked = (value === '1' || value === 'true' || value === 'yes') ? ' checked' : '';
return '<input type="checkbox" name="' + name + '" value="1"' + checked + '>';
default:
return '<input type="text" name="' + name + '" class="flat minwidth300" value="' + this.escapeHtml(value) + '"' + required + '>';
}
},
escapeHtml: function(text) {
if (!text) return '';
var div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
};
// Initialize on DOM ready
$(document).ready(function() {
KundenKarte.Tree.init();
KundenKarte.Favorites.init();
KundenKarte.SystemTabs.init();
KundenKarte.IconPicker.init();
KundenKarte.DynamicFields.init();
});
})();