Skip to content

Commit 4ce5169

Browse files
committed
feat: tour moduli articoli, contratti, ddt, fatture, impianti, interventi, ordini, preventivi e guida ai filtri in scadenzario
1 parent 5c32650 commit 4ce5169

19 files changed

Lines changed: 2854 additions & 117 deletions

File tree

controller.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,16 @@
6262
<button type="button" class="btn btn-primary" data-widget="modal" data-title="'.tr('Aggiungi').'..." data-href="add.php?id_module='.$id_module.'&id_plugin='.$id_plugin.'"><i class="fa fa-plus"></i></button>';
6363
}
6464

65+
// Pulsante per il tour guidato (se esiste il file tour nel modulo)
66+
$guide_file = base_dir().'/modules/'.$structure->directory.'/js/'.$structure->directory.'-guide.js';
67+
if (file_exists($guide_file)) {
68+
$guide_function = 'show'.ucfirst($structure->directory).'Guide()';
69+
echo '
70+
<button type="button" class="btn btn-info btn-md" onclick="'.$guide_function.'" title="Avvia tour guidato">
71+
<i class="fa fa-question-circle"></i>
72+
</button>';
73+
}
74+
6575
echo '
6676
</h1>
6777
</div>

modules/articoli/edit.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,3 +525,5 @@ function caricaDocumentiCollegati() {
525525
$("#prezzivendita").load("<?php echo base_path_osm(); ?>/ajax_complete.php?module=Articoli&op=getprezzivendita&idarticolo="+ <?php echo $id_record; ?> + "&limit=20");
526526
});
527527
</script>
528+
529+
<script type="text/javascript" src="<?php echo $rootdir; ?>/modules/articoli/js/articoli-tour.js"></script>
Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
/**
2+
* Tour guidato del modulo Articoli
3+
* Utilizza Shepherd.js per guidare l'utente attraverso le funzionalità principali
4+
*/
5+
6+
let articoliTour = null;
7+
8+
function initArticoliTour() {
9+
if (typeof Shepherd === 'undefined') {
10+
console.error('Shepherd.js non è disponibile. Attendi il caricamento della libreria...');
11+
setTimeout(function() {
12+
if (typeof Shepherd === 'undefined') {
13+
console.error('Shepherd.js non è disponibile dopo il ritardo. Il tour non può essere inizializzato.');
14+
return;
15+
} else {
16+
console.log('Shepherd.js è ora disponibile. Inizializzazione del tour...');
17+
initArticoliTourInternal();
18+
}
19+
}, 500);
20+
return;
21+
}
22+
23+
initArticoliTourInternal();
24+
}
25+
26+
function initArticoliTourInternal() {
27+
articoliTour = new Shepherd.Tour({
28+
tourName: 'articoli-tour',
29+
useModalOverlay: true,
30+
defaultStepOptions: {
31+
classes: 'shadow-md bg-purple-dark',
32+
scrollTo: { behavior: 'smooth', block: 'center' },
33+
cancelIcon: {
34+
enabled: true,
35+
label: 'Chiudi'
36+
},
37+
arrow: true,
38+
modalOverlayOpeningPadding: 10,
39+
modalOverlayOpeningRadius: 10,
40+
},
41+
});
42+
43+
addTourSteps();
44+
45+
articoliTour.on('complete', function() {
46+
localStorage.setItem('articoli-tour-completed', 'true');
47+
showTourCompleteMessage();
48+
});
49+
50+
articoliTour.on('cancel', function() {
51+
console.log('Tour articoli cancellato');
52+
});
53+
}
54+
55+
function findElementBySelector(selector, containsText) {
56+
try {
57+
if (typeof jQuery === 'undefined' && typeof $ === 'undefined') {
58+
console.error('jQuery non è disponibile');
59+
return null;
60+
}
61+
62+
const $ = typeof jQuery !== 'undefined' ? jQuery : window.$;
63+
64+
if (containsText) {
65+
const $element = $(selector).filter(function() {
66+
return $(this).text().includes(containsText);
67+
});
68+
69+
if ($element.length > 0) {
70+
return $element[0];
71+
}
72+
return null;
73+
} else {
74+
const $element = $(selector);
75+
if ($element.length > 0) {
76+
return $element[0];
77+
}
78+
return null;
79+
}
80+
} catch (error) {
81+
console.error('Errore nella ricerca dell\'elemento:', error);
82+
return null;
83+
}
84+
}
85+
86+
function addTourSteps() {
87+
articoliTour.addStep({
88+
id: 'introduction',
89+
title: 'Benvenuto nel modulo Articoli',
90+
text: `
91+
<div class="tour-step">
92+
<p>Vuoi iniziare il tour guidato del modulo Articoli?</p>
93+
<p>Il tour ti guiderà attraverso le sezioni principali del modulo.</p>
94+
</div>
95+
`,
96+
attachTo: {
97+
element: document.body,
98+
on: 'top'
99+
},
100+
buttons: [
101+
{
102+
text: 'No',
103+
action: articoliTour.cancel,
104+
classes: 'shepherd-button-secondary'
105+
},
106+
{
107+
text: 'Inizia il tour',
108+
action: articoliTour.next,
109+
classes: 'shepherd-button-primary'
110+
}
111+
]
112+
});
113+
114+
const articoloElement = findElementBySelector('.card-title', 'Articolo');
115+
articoliTour.addStep({
116+
id: 'articolo',
117+
title: 'Dati Articolo',
118+
text: `
119+
<div class="tour-step">
120+
<p>Definisci il codice, la descrizione e la categoria. Carica un'immagine e attiva l'articolo se necessario.</p>
121+
<p><strong>Dettagli prodotto:</strong> Seleziona marca e modello, definisci sottocategoria, ubicazione, unità di misura, garanzia e i dati fisici (peso/volume).</p>
122+
</div>
123+
`,
124+
attachTo: articoloElement ? {
125+
element: articoloElement.closest('.card'),
126+
on: 'bottom'
127+
} : null,
128+
buttons: [
129+
{
130+
text: 'Fine tour',
131+
action: completeTourAndClose,
132+
classes: 'shepherd-button-secondary'
133+
},
134+
{
135+
text: 'Indietro',
136+
action: articoliTour.back
137+
},
138+
{
139+
text: 'Avanti',
140+
action: articoliTour.next
141+
}
142+
]
143+
});
144+
145+
const acquistoElement = findElementBySelector('.card-title', 'Acquisto');
146+
articoliTour.addStep({
147+
id: 'acquisto',
148+
title: 'Acquisto',
149+
text: `
150+
<div class="tour-step">
151+
<p><strong>Fornitore:</strong></p>
152+
<ul>
153+
<li><strong>Fornitore predefinito:</strong> Fornitore predefinito tra quelli presenti nel plugin "Listino fornitori"</li>
154+
<li><strong>Conto predefinito:</strong> Conto di acquisto predefinito</li>
155+
</ul>
156+
<p><strong>Prezzo:</strong></p>
157+
<ul>
158+
<li><strong>Prezzo di acquisto:</strong> Prezzo previsto per i fornitori</li>
159+
</ul>
160+
<p><em>Nota: I prezzi di acquisto vengono utilizzati dai fornitori per generare i listini di acquisto.</em></p>
161+
</div>
162+
`,
163+
attachTo: acquistoElement ? {
164+
element: acquistoElement.closest('.card'),
165+
on: 'bottom'
166+
} : null,
167+
buttons: [
168+
{
169+
text: 'Fine tour',
170+
action: completeTourAndClose,
171+
classes: 'shepherd-button-secondary'
172+
},
173+
{
174+
text: 'Indietro',
175+
action: articoliTour.back
176+
},
177+
{
178+
text: 'Avanti',
179+
action: articoliTour.next
180+
}
181+
]
182+
});
183+
184+
const venditaElement = findElementBySelector('.card-title', 'Vendita');
185+
articoliTour.addStep({
186+
id: 'vendita',
187+
title: 'Vendita',
188+
text: `
189+
<div class="tour-step">
190+
<p><strong>Prezzo di vendita:</strong></p>
191+
<ul>
192+
<li><strong>Coefficiente di vendita:</strong> Moltiplicatore per calcolare automaticamente il prezzo di vendita</li>
193+
<li><strong>Prezzo di vendita:</strong> Prezzo di vendita ${document.location.href.includes('prezzi_ivati') ? 'IVA inclusa' : 'esclusa IVA'}</li>
194+
<li><strong>Iva di vendita:</strong> Aliquota IVA applicata (se non specificata usa l'IVA predefinita)</li>
195+
<li><strong>Minimo di vendita:</strong> Quantità minima vendibile</li>
196+
</ul>
197+
<p><strong>Conto:</strong></p>
198+
<ul>
199+
<li><strong>Conto predefinito di vendita:</strong> Conto di vendita predefinito</li>
200+
</ul>
201+
<p><em>Nota: Premi il pulsante "Scorpora l'IVA" se vuoi calcolare il prezzo di vendita senza IVA.</em></p>
202+
</div>
203+
`,
204+
attachTo: venditaElement ? {
205+
element: venditaElement.closest('.card'),
206+
on: 'bottom'
207+
} : null,
208+
buttons: [
209+
{
210+
text: 'Fine tour',
211+
action: completeTourAndClose,
212+
classes: 'shepherd-button-secondary'
213+
},
214+
{
215+
text: 'Indietro',
216+
action: articoliTour.back
217+
},
218+
{
219+
text: 'Avanti',
220+
action: articoliTour.next
221+
}
222+
]
223+
});
224+
225+
articoliTour.addStep({
226+
id: 'storico',
227+
title: 'Storico Prezzi',
228+
text: `
229+
<div class="tour-step">
230+
<p><strong>Ultimi 20 prezzi:</strong></p>
231+
<ul>
232+
<li><strong>Ultimi 20 prezzi di acquisto:</strong> Storico degli ultimi prezzi di acquisto da fornitori</li>
233+
<li><strong>Ultimi 20 prezzi di vendita:</strong> Storico degli ultimi prezzi di vendita praticati</li>
234+
</ul>
235+
<p><em>Nota: Lo storico ti aiuta a monitorare i trend dei prezzi e a verificare la competitività.</em></p>
236+
</div>
237+
`,
238+
attachTo: {
239+
element: document.querySelector('#prezziacquisto')?.closest('.card'),
240+
on: 'bottom'
241+
},
242+
buttons: [
243+
{
244+
text: 'Fine tour',
245+
action: completeTourAndClose,
246+
classes: 'shepherd-button-secondary'
247+
},
248+
{
249+
text: 'Indietro',
250+
action: articoliTour.back
251+
},
252+
{
253+
text: 'Termina il tour',
254+
action: articoliTour.complete
255+
}
256+
]
257+
});
258+
}
259+
260+
function startArticoliTour() {
261+
if (!articoliTour) {
262+
initArticoliTour();
263+
}
264+
265+
if (articoliTour) {
266+
articoliTour.start();
267+
}
268+
}
269+
270+
function showTourCompleteMessage() {
271+
if (typeof swal !== 'undefined') {
272+
swal({
273+
title: 'Tour Completato',
274+
text: 'Hai completato il tour guidato del modulo Articoli. Ora sei pronto per utilizzare tutte le funzionalità!',
275+
type: 'success',
276+
confirmButtonText: 'Perfetto',
277+
confirmButtonClass: 'btn-success'
278+
});
279+
} else {
280+
alert('Tour Completato. Hai completato il tour guidato del modulo Articoli.');
281+
}
282+
}
283+
284+
function completeTourAndClose() {
285+
localStorage.setItem('articoli-tour-completed', 'true');
286+
287+
if (articoliTour) {
288+
articoliTour.cancel();
289+
}
290+
}
291+
292+
function isTourCompleted() {
293+
return localStorage.getItem('articoli-tour-completed') === 'true';
294+
}
295+
296+
function showRestartTourButton() {
297+
const restartButton = `
298+
<button type="button" class="btn btn-info btn-xs" onclick="startArticoliTour()" title="Riavvia il tour guidato">
299+
<i class="fa fa-question-circle"></i> Tour guidato
300+
</button>
301+
`;
302+
303+
$('.content-header .btn-group').after(restartButton);
304+
}
305+
306+
function initTour() {
307+
if ($('#edit-form').length > 0) {
308+
showRestartTourButton();
309+
310+
if (!isTourCompleted()) {
311+
setTimeout(function() {
312+
startArticoliTour();
313+
}, 1000);
314+
}
315+
}
316+
}
317+
318+
if (document.readyState === 'loading') {
319+
$(document).ready(initTour);
320+
} else {
321+
initTour();
322+
}

0 commit comments

Comments
 (0)