feat: add previous configuration handling for user selections in quotes

- Implemented session storage management for the last configuration choices made by the user.
- Added functionality to prompt users when changing models to retain their previous selections.
- Introduced methods to apply previous choices to the newly loaded model, including finishes, handles, glass types, and options.
- Enhanced user experience by ensuring selections are automatically applied where applicable.
This commit is contained in:
2026-10-19 19:49:44 +02:00
parent 863e6ca920
commit 06617a985a
197 changed files with 4892 additions and 400 deletions
+52 -16
View File
@@ -13,6 +13,7 @@ import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap/dist/js/bootstrap.bundle.min.js';
import Configurator from './configurator.js';
import AuthGate from './auth_gate.js';
import ConfigurazionePrecedente from './configurazione_precedente.js';
import { API_BASE as PREVENTIVI_API_BASE } from './api_base.js';
@@ -46,6 +47,17 @@ const configReady = (async () => {
let invioPreventivoInCorso = false;
// Conferma non bloccante (markup fisso #toast in index.html).
let timerToast = null;
const mostraToast = (messaggio) => {
const toast = document.getElementById('toast');
if (!toast) return;
toast.querySelector('.toast-messaggio').textContent = messaggio;
toast.classList.add('visibile');
clearTimeout(timerToast);
timerToast = setTimeout(() => toast.classList.remove('visibile'), 3000);
};
// Mostra/aggiorna il link "Vai al preventivo" (markup fisso in index.html, sopra la cart-container).
window.mostraLinkPreventivo = (token, count) => {
const link = document.getElementById('vai-al-preventivo');
@@ -88,6 +100,8 @@ ripristinaLinkPreventivo();
$(() => {
ConfigurazionePrecedente.intercettaCambioModello();
$('.cart-button').on('click', async () => {
if ($('.cart-button').hasClass('disabled') || invioPreventivoInCorso)
@@ -97,18 +111,16 @@ $(() => {
try {
const params = ParamsParser.getQueryParams();
const img = await window.generaImagine();
const conf = window.configurazione || {};
const inner = conf.configurazione || {};
const inner = window.configurazione?.configurazione || {};
// Prezzo e descrizione delle scelte li ricalcola il backoffice: si inviano solo i codici
// (letti ora dal menu) e le voci descrittive che non incidono sul prezzo.
const body = {
token: localStorage.getItem(CART_TOKEN_KEY) || undefined,
sistema: params.sistema,
type: params.type,
nome: inner.nome,
larghezza: parseInt(inner.width) || null,
altezza: parseInt(inner.height) || null,
qty: parseInt($('.quantity-input').val()) || 1,
prezzo: conf.prezzo,
scelte: Configurator.raccogliScelte(),
payload: inner,
img: img,
};
@@ -124,6 +136,8 @@ $(() => {
const data = await res.json();
localStorage.setItem(CART_TOKEN_KEY, data.token);
window.mostraLinkPreventivo(data.token, data.carrello.items.length);
ConfigurazionePrecedente.salva(params.sistema, params.type, body.scelte);
mostraToast('Prodotto aggiunto al preventivo');
} catch (e) {
console.error(e);
alert('Si è verificato un errore nell\'aggiunta al preventivo. Riprova.');
@@ -136,7 +150,7 @@ $(() => {
const start = async () => {
const params = ParamsParser.getQueryParams();
// Sistemi e modelli (con icone) sono serviti dal backoffice, già filtrati sui soli attivi
// Sistemi e modelli (con icone) sono serviti dal backoffice, già filtrati sui soli attivi con listino
// e nell'ordine del menu: il primo sistema è quello di default.
const res = await fetch(`${PREVENTIVI_API_BASE}/sistemi`, { headers: { Accept: 'application/json' } });
if (!res.ok) throw new Error(`Catalogo non disponibile (HTTP ${res.status})`);
@@ -168,23 +182,29 @@ const start = async () => {
window.history.replaceState(null, '', url);
}
const data = await (await fetch(`${modello_folder}/${modello.cartella}/form.json?rnd=${Math.random()}`)).json();
const data_finiture = modello.moduli.includes('modello') ? (await (await fetch(`${modello_folder}/${modello.cartella}/modello.json?rnd=${Math.random()}`)).json()).filter(el => el.id != "") : [];
const data_maniglie = modello.moduli.includes('maniglia') ? (await (await fetch(`${modello_folder}/${modello.cartella}/maniglia.json?rnd=${Math.random()}`)).json()).filter(el => el.id != "") : [];
// Menu, finiture, maniglie, vetri, opzioni (con le immagini) e misure del listino dal backoffice.
const resConfigurazione = await fetch(`${PREVENTIVI_API_BASE}/sistemi/${encodeURIComponent(params.sistema)}/modelli/${encodeURIComponent(modello.id)}/configurazione`, { headers: { Accept: 'application/json' } });
if (!resConfigurazione.ok) throw new Error(`Configurazione non disponibile per il modello ${modello.id} (HTTP ${resConfigurazione.status})`);
const configurazione = await resConfigurazione.json();
const data = configurazione.form;
const data_finiture = configurazione.finiture;
const data_maniglie = configurazione.maniglie;
const data_vetri = configurazione.vetri;
const listino = configurazione.misure;
const data_avvolgibile = modello.moduli.includes('avvolgibile') ? (await (await fetch(`${modello_folder}/${modello.cartella}/avvolgibile.json?rnd=${Math.random()}`)).json()).filter(el => el.id != "") : [];
const data_vetri = modello.moduli.includes('vetro') ? (await (await fetch(`${modello_folder}/${modello.cartella}/vetro.json?rnd=${Math.random()}`)).json()).filter(el => el.id != "") : [];
const listino = modello.moduli.includes('listino')
? await (await fetch(`${PREVENTIVI_API_BASE}/sistemi/${encodeURIComponent(params.sistema)}/modelli/${encodeURIComponent(modello.id)}/listino`, { headers: { Accept: 'application/json' } })).json()
: [];
if (modello.moduli.includes('listino') && !listino.length)
throw new Error(`Nessun listino caricato per il modello ${modello.id}`);
const altezza_maniglia = modello.moduli.includes('altezza_maniglia') ? await (await fetch(`${data_folder}/altezza_maniglia_${modello.categoria}.json?rnd=${Math.random()}`)).json() : [];
let priceCalculator = await MenuBuilder.build(data, 'left-container', modello.cartella_texture || modello.cartella, {
let priceCalculator = await MenuBuilder.build(data, 'left-container', modello.id, {
modelli: modelli,
categoria: modello.categoria,
sistema: params.sistema,
data_finiture: data_finiture,
// Finiture attive nel modello per lato (menu finiture interne / esterne).
data_finiture_interne: configurazione.finiture_interne,
data_finiture_esterne: configurazione.finiture_esterne,
data_maniglie: data_maniglie,
data_avvolgibile: data_avvolgibile,
data_vetri: data_vetri,
@@ -194,7 +214,8 @@ const start = async () => {
Configurator.init(priceCalculator, {
modello: modello,
nome_esteso: data.nome,
sistema: params.sistema,
form: data,
data_finiture: data_finiture,
data_maniglie: data_maniglie,
data_avvolgibile: data_avvolgibile,
@@ -249,6 +270,9 @@ const start = async () => {
$("#larghezza").first().trigger("change");
// Scelte del modello precedente, se l'utente ha chiesto di mantenerle.
ConfigurazionePrecedente.applica();
//player.addMarker("Cylinder028", 'left', 'https://www.youtube.com/embed/l_zqI0v7JcU?si=Vq2k8nWHYk13esMQ');
//player.addMarker("marker_1", 'left', 'https://www.youtube.com/embed/l_zqI0v7JcU?si=Vq2k8nWHYk13esMQ');
//player.addMarker("marker_2", 'right', 'https://www.youtube.com/embed/LCi9jdF_4MQ?si=b8cyxSOI2sKEmmcm');
@@ -368,6 +392,18 @@ const start = async () => {
render();
}
// Mostra / nasconde le mesh di una categoria (es. maniglia col motore): usate dalle opzioni
// dei menu a tendina tramite i parametri func e categoria impostati nel backoffice.
window.mostraCategoria = (categoria) => {
player.showObject(categoria);
render();
}
window.nascondiCategoria = (categoria) => {
player.hideObject(categoria);
render();
}
window.nascondiCerniere = (categoria) => {
player.hideObject(categoria);
render();