1056 lines
45 KiB
JavaScript
Executable file
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">×</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;">×</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();
|
|
});
|
|
|
|
})();
|