diff --git a/assets/src/css/style.css b/assets/src/css/style.css index ce3c6ff71..0f3c87898 100755 --- a/assets/src/css/style.css +++ b/assets/src/css/style.css @@ -81,7 +81,8 @@ input[type=file] { position: fixed; } -.ui-autocomplete { +.autocomplete, .ui-autocomplete { + background: white; z-index: 10000; min-width: 160px; padding: 10px; @@ -94,8 +95,23 @@ input[type=file] { border-radius: 5px; } -.ui-autocomplete-category { - font-size: 150%; +.autocomplete .group, .ui-autocomplete-category { + font-size: 1.5em; + background: inherit; +} + +.autocomplete > div { + padding: 5px; +} + +.highlight { + background: #FFFF66; +} + +.autocomplete > div:hover:not(.group), +.autocomplete > div.selected { + background: #F5F5F5; + cursor: pointer; } .ui-autocomplete-scrollable { @@ -1069,6 +1085,14 @@ div.tip { background-color: rgba(255, 99, 71, 0.6) !important; } -.login-box .img-responsive{ +.login-box .img-responsive { padding: 18px 0px 4px; } + +.no-selection { + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} diff --git a/assets/src/js/base/datatables-buttons.js b/assets/src/js/base/datatables-buttons.js index ce35069f5..9838dd939 100644 --- a/assets/src/js/base/datatables-buttons.js +++ b/assets/src/js/base/datatables-buttons.js @@ -138,7 +138,7 @@ $(document).ready(function () { $(this).attr("data-id_records", ""); $(this).data("id_records", ""); } else { - swal(globals.translations.waiting, globals.translations.waiting_msg, "error"); + swal(globals.translations.waiting, globals.translations.waitingMessage, "error"); } }); }); diff --git a/assets/src/js/base/sidebar.js b/assets/src/js/base/sidebar.js index 906b847aa..c0c292e88 100644 --- a/assets/src/js/base/sidebar.js +++ b/assets/src/js/base/sidebar.js @@ -40,21 +40,21 @@ $(document).ready(function () { } // Menu ordinabile - $(".sidebar-menu").sortable({ - cursor: 'move', + if (!globals.is_mobile) { + sortable(".sidebar-menu", { + axis: "y", + cursor: "move", + dropOnEmpty: true, + scroll: true, + })[0].addEventListener("sortupdate", function(e) { + let order = $(".sidebar-menu > .treeview[data-id]").toArray().map(a => $(a).data("id")) - stop: function (event, ui) { - let order = $(this).sortable('toArray').toString(); - - $.post(globals.rootdir + "/actions.php?id_module=" + globals.order_manager_id, { - op: 'sort_modules', - ids: order + $.post(globals.rootdir + "/actions.php", { + id_module: globals.order_manager_id, + op: "sort_modules", + order: order.join(","), }); - } - }); - - if (globals.is_mobile) { - $(".sidebar-menu").sortable("disable"); + }); } $(".sidebar-toggle").click(function () { diff --git a/assets/src/js/base/supersearch.js b/assets/src/js/base/supersearch.js index 37f153c09..23212355a 100644 --- a/assets/src/js/base/supersearch.js +++ b/assets/src/js/base/supersearch.js @@ -17,88 +17,75 @@ */ $(document).ready(function () { - $('#supersearch').keyup(function () { - $(document).ajaxStop(); + const searchInput = $('#supersearch'); + const searchButton = searchInput.parent().find('i'); + const searches = []; - if ($(this).val() == '') { - $(this).removeClass('wait'); - } else { - $(this).addClass('wait'); - } - }); + autocomplete({ + minLength: 1, + input: searchInput[0], + emptyMsg: globals.translations.noResults, + debounceWaitMs: 500, + fetch: function(text, update) { + text = text.toLowerCase(); - $.widget("custom.supersearch", $.ui.autocomplete, { - _create: function () { - this._super(); - this.widget().menu("option", "items", "> :not(.ui-autocomplete-category)"); - }, - _renderMenu: function (ul, items) { - if (items[0].value == undefined) { - $('#supersearch').removeClass('wait'); - ul.html(''); - } else { - var that = this, - currentCategory = ""; + // Registrazione ricerca + searches.push(text); + searchButton + .removeClass('fa-search') + .addClass('fa-spinner fa-spin'); - ul.addClass('ui-autocomplete-scrollable'); - ul.css('z-index', '999'); - - $.each(items, function (index, item) { - - if (item.category != currentCategory) { - ul.append("
  • " + item.category + "
  • "); - currentCategory = item.category; - } - - that._renderItemData(ul, item); - }); - } - }, - _renderItem: function (ul, item) { - return $("
  • ") - .append("" + item.value + "
    " + item.label + "
    ") - .appendTo(ul); - } - }); - - // Configurazione supersearch - var $super = $('#supersearch').supersearch({ - minLength: 3, - select: function (event, ui) { - location.href = ui.item.link; - }, - source: function (request, response) { $.ajax({ url: globals.rootdir + '/ajax_search.php', - dataType: "json", + dataType: "JSON", data: { - term: request.term + term: text, }, - - complete: function (jqXHR) { - $('#supersearch').removeClass('wait'); - }, - success: function (data) { - if (data == null) { - response($.map(['a'], function (item) { - return false; - })); - } else { - response($.map(data, function (item) { - labels = (item.labels).toString(); - labels = labels.replace('
    ,', '
    '); + // Fix per gestione risultati null + data = data ? data : []; - return { - label: labels, - category: item.category, - link: item.link, - value: item.title - } - })); + // Trasformazione risultati in formato leggibile + const results = data.map(function (result) { + return { + label: result.label ? result.label : '

    ' + result.title + '

    ' + result.labels + .join('').split('
    ,').join('
    '), + group: result.category, + link: result.link, + value: result.title + } + }); + + // Rimozione ricerca in corso + searches.pop(); + if (searches.length === 0) { + searchButton + .removeClass('fa-spinner fa-spin') + .addClass('fa-search'); } + + update(results); + }, + error: function (){ + searchButton + .removeClass('fa-spinner fa-spin') + .addClass('fa-exclamation-triangle'); } }); + }, + preventSubmit: true, + disableAutoSelect: true, + onSelect: function(item) { + window.location.href = item.link; + }, + customize: function(input, inputRect, container, maxHeight) { + container.style.width = '600px'; + }, + render: function(item, currentValue){ + const itemElement = document.createElement("div"); + itemElement.innerHTML = item.label; + // " + item.value + "
    " + item.label + "
    + return itemElement; } }); }); diff --git a/assets/src/js/base/widgets.js b/assets/src/js/base/widgets.js index 4736df14a..3aae07b43 100644 --- a/assets/src/js/base/widgets.js +++ b/assets/src/js/base/widgets.js @@ -17,35 +17,36 @@ */ $(document).ready(function () { - $("#widget-top, #widget-right").sortable({ + const widgets = sortable("#widget-top, #widget-right", { + forcePlaceholderSize: true, items: 'li', cursor: 'move', dropOnEmpty: true, - connectWith: '.widget', + acceptFrom: '.widget', scroll: true, - helper: 'clone', - start: function (event, ui) { - // Salvo la lista da cui proviene il drag - src_list = ($(this).attr('id')).replace('widget-', ''); + }); - // Evidenzio le aree dei widget - $('.widget').addClass('bordered').sortable('refreshPositions'); - }, - stop: function (event, ui) { + for (const sorting of widgets) { + sorting.addEventListener("sortupdate", function (e) { // Rimuovo l'evidenziazione dell'area widget $('.widget').removeClass('bordered'); // Salvo la lista su cui ho eseguito il drop - dst_list = (ui.item.parent().attr('id')).replace('widget-', ''); + const location = $(e.detail.destination.container).attr('id').replace('widget-', ''); - var order = $(this).sortable('toArray').toString(); - $.post(globals.rootdir + "/actions.php?id_module=" + globals.order_manager_id, { - op: 'sort_widgets', - location: dst_list, - ids: order, + let order = $(".widget li[data-id]").toArray().map(a => $(a).data("id")) + $.post(globals.rootdir + "/actions.php", { + id_module: globals.order_manager_id, id_module_widget: globals.id_module, - id_record: globals.id_record, + op: 'sort_widgets', + location: location, + order: order.join(','), }); - } - }); + }); + + sorting.addEventListener("sortstart", function (e) { + // Evidenzio le aree dei widget + $('.widget').addClass('bordered'); + }); + } }); diff --git a/assets/src/js/functions/allegati.js b/assets/src/js/functions/allegati.js new file mode 100644 index 000000000..f15708843 --- /dev/null +++ b/assets/src/js/functions/allegati.js @@ -0,0 +1,308 @@ +/* + * OpenSTAManager: il software gestionale open source per l'assistenza tecnica e la fatturazione + * Copyright (C) DevCode s.r.l. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// Disabling autoDiscover, otherwise Dropzone will try to attach twice. +Dropzone.autoDiscover = false; + +/** + * Restituisce filename ed estensione di un file indicato. + * @param path + * @returns [string, string] + */ +function getFilenameAndExtension(path) { + let filename_extension = path.replace(/^.*[\\\/]/, ''); + let filename = filename_extension.substring(0, filename_extension.lastIndexOf('.')); + let ext = filename_extension.split('.').pop(); + + return [filename, ext]; +} + +/** + * Inizializza la gestione degli allegati. + * @param gestione + */ +function initGestioneAllegati(gestione) { + const dropzone_id = '#' + gestione.attr('id') + ' .dropzone'; + const maxFilesize = gestione.data('max_filesize'); + if ($(dropzone_id).length === 0) { + return; + } + + let params = new URLSearchParams({ + op: "aggiungi-allegato", + id_module: gestione.data('id_module'), + id_plugin: gestione.data('id_plugin'), + id_record: gestione.data('id_record'), + }).toString(); + + let dragdrop = new Dropzone(dropzone_id, { + dictDefaultMessage: globals.translations.allegati.messaggio + ".
    (" + globals.translations.allegati.maxFilesize.replace('_SIZE_', maxFilesize) + ")", + paramName: "file", + maxFilesize: maxFilesize, // MB + uploadMultiple: false, + parallelUploads: 2, + addRemoveLinks: false, + autoProcessQueue: true, + autoQueue: true, + url: globals.rootdir + "/actions.php?" + params, + init: function (file, xhr, formData) { + this.on("success", function (file) { + dragdrop.removeFile(file); + }); + + this.on("complete", function (file) { + // Ricarico solo quando ho finito + if (this.getUploadingFiles().length === 0 && this.getQueuedFiles().length === 0) { + ricaricaAllegati(gestione); + } + }); + } + }); +} + +/** + * Funzione per l'apertura della schermata di modifica per una categoria di allegati. + * @param gestione + * @param pulsanteModifica + */ +function modificaCategoriaAllegati(gestione, pulsanteModifica) { + const categoria = $(pulsanteModifica).parent().parent(); + console.log(categoria) + + const nome = categoria.find(".box-title"); + nome.addClass('hidden'); + $(pulsanteModifica).addClass('hidden'); + + const pulsanteSalva = categoria.find(".category-save"); + const pulsanteAnnulla = categoria.find(".category-cancel"); + const inputNome = categoria.find(".category-name"); + pulsanteSalva.removeClass("hidden"); + pulsanteAnnulla.removeClass("hidden"); + inputNome.removeClass("hidden"); +} + +/** + * Funzione per salvare le modifiche effettuate su una categoria di allegati. + * @param gestione + * @param pulsanteSalva + */ +function salvaCategoriaAllegati(gestione, pulsanteSalva) { + const categoria = $(pulsanteSalva).parent().parent(); + + const nome = categoria.find(".box-title"); + const inputNome = categoria.find(".category-name"); + + mostraCaricamentoAllegati(gestione); + + $.ajax({ + url: globals.rootdir + "/actions.php", + cache: false, + type: "POST", + data: { + op: "modifica-categoria-allegato", + id_module: gestione.data('id_module'), + id_plugin: gestione.data('id_plugin'), + id_record: gestione.data('id_record'), + category: nome.text(), + name: inputNome.val(), + }, + success: function (data) { + ricaricaAllegati(gestione); + }, + error: function (gestione) { + ricaricaAllegati(gestione); + } + }); +} + +/** + * Funzione per caricare un nuovo allegato. + * @param gestione + */ +function aggiungiAllegato(gestione) { + const id = "#" + gestione.attr('id'); + const form = $(id + " #upload-form"); + + form.ajaxSubmit({ + url: globals.rootdir + "/actions.php", + data: data, + type: "post", + uploadProgress: function (event, position, total, percentComplete) { + $(id + " #upload").prop("disabled", true).html(percentComplete + "%").removeClass("btn-success").addClass("btn-info"); + }, + success: function (data) { + ricaricaAllegati(gestione); + }, + error: function (data) { + alert(globals.translations.allegati.errore + ": " + data); + } + }); +} + +/** + * Funzione per mostrare il loader di caricamento per gli allegati. + * @param gestione + */ +function mostraCaricamentoAllegati(gestione) { + const id = "#" + gestione.attr('id'); + + localLoading($(id + " .panel-body"), true); +} + +/** + * Funzione dedicata al caricamento dinamico degli allegati. + * @param gestione + */ +function ricaricaAllegati(gestione) { + const id = "#" + gestione.attr('id'); + + let params = new URLSearchParams({ + op: "list_attachments", + id_module: gestione.data('id_module'), + id_plugin: gestione.data('id_plugin'), + id_record: gestione.data('id_record'), + }).toString(); + + $(id).load(globals.rootdir + "/ajax.php?" + params, function () { + localLoading($(id + " .panel-body"), false); + + const nuovoAllegato = $(id + " table tr").eq(-1).attr("id"); + if (nuovoAllegato !== undefined) { + $("#" + nuovoAllegato).effect("highlight", {}, 1500); + } + }); +} + +/** + * Funzione per l'apertura della pagina di gestione dei dati dell'allegato. + * @param button + */ +function modificaAllegato(button) { + const gestione = $(button).closest(".gestione-allegati"); + const allegato = $(button).closest("tr").data(); + + let params = new URLSearchParams({ + op: "visualizza-modifica-allegato", + id_module: gestione.data('id_module'), + id_plugin: gestione.data('id_plugin'), + id_record: gestione.data('id_record'), + id_allegato: allegato.id, + }).toString(); + + openModal(globals.translations.allegati.modifica, globals.rootdir + "/actions.php?" + params); +} + +/** + * Funzione per gestire il download di un allegato. + * @param button + */ +function saggiungiAllegato(button) { + const gestione = $(button).closest(".gestione-allegati"); + const allegato = $(button).closest("tr").data(); + + let params = new URLSearchParams({ + op: "download-allegato", + id_module: gestione.data('id_module'), + id_plugin: gestione.data('id_plugin'), + id_record: gestione.data('id_record'), + id: allegato.id, + filename: allegato.filename, + }).toString(); + + window.open(globals.rootdir + "/actions.php?" + params, "_blank") +} + +/** + * Funzione per l'apertura dell'anteprima di visualizzazione allegato. + * @param button + */ +function visualizzaAllegato(button) { + const allegato = $(button).closest("tr").data(); + + let params = new URLSearchParams({ + file_id: allegato.id, + }).toString(); + + openModal(allegato.nome + ' (' + allegato.filename + ')', globals.rootdir + "/view.php?" + params); +} + +/** + * Funzione per la gestione della rimozione di un allegato specifico. + * + * @param button + */ +function rimuoviAllegato(button) { + const gestione = $(button).closest(".gestione-allegati"); + const allegato = $(button).closest("tr").data(); + + swal({ + title: globals.translations.allegati.elimina, + type: "warning", + showCancelButton: true, + confirmButtonText: globals.translations.allegati.procedi, + }).then(function () { + mostraCaricamentoAllegati(gestione); + + // Parametri della richiesta AJAX + let params = new URLSearchParams({ + op: "rimuovi-allegato", + id_module: gestione.data('id_module'), + id_plugin: gestione.data('id_plugin'), + id_record: gestione.data('id_record'), + id_allegato: allegato.id, + filename: allegato.filename, + }).toString(); + + // Richiesta AJAX + $.ajax(globals.rootdir + "/actions.php?" + params) + .then(function () { + ricaricaAllegati(gestione); + }); + }).catch(swal.noop); +} + +function impostaCategorieAllegatiDisponibili(gestione, categorie) { + // Disabilitazione per rimozione input in aggiunta + return; + + const id = "#" + gestione.attr('id'); + const input = $("#modifica-allegato #categoria_allegato")[0]; + + autocomplete({ + minLength: 0, + input: input, + emptyMsg: globals.translations.noResults, + fetch: function (text, update) { + text = text.toLowerCase(); + const suggestions = categorie.filter(n => n.toLowerCase().startsWith(text)); + + // Trasformazione risultati in formato leggibile + const results = suggestions.map(function (result) { + return { + label: result, + value: result + } + }); + + update(results); + }, + onSelect: function (item) { + input.value = item.label; + }, + }); +} diff --git a/assets/src/js/functions/form.js b/assets/src/js/functions/form.js index fc249190c..a415454f2 100644 --- a/assets/src/js/functions/form.js +++ b/assets/src/js/functions/form.js @@ -158,6 +158,7 @@ function salvaForm(form, data = {}, button = null) { buttonRestore(button, restore); reject(); + return; } // Gestione grafica di salvataggio diff --git a/assets/src/js/functions/functions.js b/assets/src/js/functions/functions.js index b0fff95b5..e77147816 100755 --- a/assets/src/js/functions/functions.js +++ b/assets/src/js/functions/functions.js @@ -52,7 +52,11 @@ function openModal(title, href) { } }); - var content = '
  • '; +
  • '; $info = array_merge($options, [ 'id' => $widget['id'], diff --git a/src/Modules.php b/src/Modules.php index 0452aca20..f411b85c9 100755 --- a/src/Modules.php +++ b/src/Modules.php @@ -357,7 +357,7 @@ class Modules if ($active) { $result .= ' active actual'; } - $result .= '" id="'.$element['id'].'"> + $result .= '" id="'.$element['id'].'" data-id="'.$element['id'].'"> '.$title.''; diff --git a/update/2_4_24.sql b/update/2_4_24.sql index 4e6752487..4535da9a9 100644 --- a/update/2_4_24.sql +++ b/update/2_4_24.sql @@ -140,4 +140,5 @@ INSERT INTO `zz_api_resources` (`id`, `version`, `type`, `resource`, `class`, `e ALTER TABLE `dt_ddt` ADD `id_ddt_trasporto_interno` INT(11) NULL, ADD FOREIGN KEY (`id_ddt_trasporto_interno`) REFERENCES `dt_ddt`(`id`) ON DELETE CASCADE; -- Aggiunto ragruppamento referenti per sede -UPDATE `zz_plugins` SET `options` = ' { \"main_query\": [ { \"type\": \"table\", \"fields\": \"Nome, Indirizzo, Città, CAP, Provincia, Referente\", \"query\": \"SELECT an_sedi.id, an_sedi.nomesede AS Nome, an_sedi.indirizzo AS Indirizzo, an_sedi.citta AS Città, an_sedi.cap AS CAP, an_sedi.provincia AS Provincia, GROUP_CONCAT(an_referenti.nome SEPARATOR \\\", \\\") AS Referente FROM an_sedi LEFT OUTER JOIN an_referenti ON idsede = an_sedi.id WHERE 1=1 AND an_sedi.idanagrafica=|id_parent| GROUP BY an_sedi.id HAVING 2=2 ORDER BY an_sedi.id DESC\"} ]}' WHERE `zz_plugins`.`name` = 'Sedi'; \ No newline at end of file +UPDATE `zz_plugins` SET `options` = ' { \"main_query\": [ { \"type\": \"table\", \"fields\": \"Nome, Indirizzo, Città, CAP, Provincia, Referente\", \"query\": \"SELECT an_sedi.id, an_sedi.nomesede AS Nome, an_sedi.indirizzo AS Indirizzo, an_sedi.citta AS Città, an_sedi.cap AS CAP, an_sedi.provincia AS Provincia, GROUP_CONCAT(an_referenti.nome SEPARATOR \\\", \\\") AS Referente FROM an_sedi LEFT OUTER JOIN an_referenti ON idsede = an_sedi.id WHERE 1=1 AND an_sedi.idanagrafica=|id_parent| GROUP BY an_sedi.id HAVING 2=2 ORDER BY an_sedi.id DESC\"} ]}' WHERE `zz_plugins`.`name` = 'Sedi'; +UPDATE `zz_group_module` SET `clause` = 'in_interventi.id IN (SELECT idintervento FROM in_interventi_tecnici WHERE idintervento=in_interventi.id AND idtecnico=|id_anagrafica| UNION SELECT id_intervento FROM in_interventi_tecnici_assegnati WHERE id_intervento=in_interventi.id AND id_tecnico=|id_anagrafica|)' WHERE `zz_group_module`.`name` = 'Mostra interventi ai tecnici coinvolti';