Cart - Gestione del Carrello β
Sistema completo per la gestione del carrello con sincronizzazione multi-tab e comunicazione bidirezionale con iframe.
Architettura β
Il sistema Γ¨ composto da quattro componenti principali:
1. Store Zustand (app/store/cart/cart.store.ts) β
Store globale che mantiene lo stato del carrello:
counter: numero di articoli nel carrellohasItems: boolean che indica se ci sono articoliisDropdownOpen: stato del dropdownshow: boolean per mostrare/nascondere il carrello
2. Componente React (app/components/layout/topbar/cart.tsx) β
Componente UI per la topbar con:
- Badge del contatore articoli
- Dropdown con azioni: Apri, Usa, Offline, Fastlabel, Svuota
- Gestione click esterni e chiusura automatica
3. Hook di Comunicazione (app/hooks/ui/useTopbarCart.ts) β
Gestisce la comunicazione bidirezionale con l'iframe:
- Registra
window.cartper chiamate dall'iframe - Invoca metodi
common_cartnell'iframe - Sincronizza lo stato tra SPA e iframe
4. Hook di Sincronizzazione (app/hooks/sync/useCartSyncListener.ts) β
Sincronizza il carrello tra tutte le tab del browser usando BroadcastChannel API
Setup β
1. Importare gli Hook nell'AuthenticatedLayout β
import { useTopbarCart } from "@/hooks/ui/useTopbarCart";
import { useCartSyncListener } from "@/hooks/sync/useCartSyncListener";
export function AuthenticatedLayout() {
useTopbarCart(); // Comunicazione con iframe
useCartSyncListener(); // Sincronizzazione tra tab
return (
<div>
{/* Layout content */}
</div>
);
}2. Aggiungere il Componente alla Topbar β
import { Cart } from "@/components/layout/topbar/cart";
export function Topbar() {
return (
<nav>
<ul>
<Cart />
{/* Altri elementi della topbar */}
</ul>
</nav>
);
}Utilizzo β
Dall'iframe (legacy code) β
L'iframe puΓ² comunicare con il carrello attraverso window.parent.cart:
Impostare il contatore β
// Imposta il numero di articoli nel carrello
window.parent.cart.setCounter(5);
// Azzera il carrello
window.parent.cart.setCounter(0);Richiedere il contenuto del carrello β
// Richiede il contenuto dettagliato del carrello
window.parent.cart.read();Aprire il carrello β
// Apre la vista del carrello nell'iframe
window.parent.cart.open();Scaricare/Usare il carrello β
// Avvia il download/uso del carrello
window.parent.cart.download();Svuotare il carrello β
// Svuota completamente il carrello
window.parent.cart.empty();Callback per clipboard β
// Chiamato dopo successo copia Fastlabel
window.parent.cart.commoncart_clipboard_success();
// Chiamato dopo successo copia Offline
window.parent.cart.commoncart_offline_clipboard_success();Dalla SPA React β
import { useCartStore } from "@/store/cart/cart.store";
function MyComponent() {
const counter = useCartStore((state) => state.counter);
const hasItems = useCartStore((state) => state.hasItems);
const setCounter = useCartStore((state) => state.setCounter);
return (
<div>
<p>Articoli nel carrello: {counter}</p>
{hasItems && <p>Hai articoli nel carrello!</p>}
<button onClick={() => setCounter(counter + 1)}>
Aggiungi articolo
</button>
</div>
);
}FunzionalitΓ β
1. Contatore Dinamico β
- Badge con numero articoli
- Evidenziazione visiva quando
hasItems = true - Titolo dinamico: "Carrello", "1 articolo", "N articoli"
2. Dropdown Interattivo β
- Si apre al click sul badge
- Richiede automaticamente il contenuto dall'iframe
- Si chiude al click esterno o su azione
- Mostra "Il carrello Γ¨ vuoto" quando
counter = 0
3. Azioni Disponibili β
Apri β
Apre la vista dettagliata del carrello nell'iframe.
Usa β
Avvia il processo di utilizzo/download del carrello.
Offline (opzionale) β
Copia il contenuto offline nella clipboard. Visibile solo se window.commoncart_btn_offline = true.
Fastlabel (opzionale) β
Copia il contenuto fastlabel nella clipboard. Visibile solo se window.commoncart_btn_fastlabel = true.
Svuota β
Svuota completamente il carrello dopo conferma.
4. Sincronizzazione Multi-Tab β
Quando un utente modifica il carrello in una tab:
- Lo store Zustand viene aggiornato
useCartSyncListenerinvia un messaggio via BroadcastChannel- Tutte le altre tab ricevono il messaggio
- Gli store nelle altre tab si sincronizzano automaticamente
5. Comunicazione Bidirezionale β
- Iframe β SPA: Chiama
window.parent.cart.setCounter(n)per aggiornare il badge - SPA β Iframe: Chiama
iframe.contentWindow.common_cart.open()per aprire il carrello
Variabili Globali β
Il componente si aspetta queste variabili su window:
interface Window {
base_url?: string; // Base URL per i link
commoncart_btn_offline?: boolean; // Mostra bottone Offline
commoncart_btn_fastlabel?: boolean; // Mostra bottone Fastlabel
}L'iframe deve esporre:
interface IframeWindow {
common_cart?: {
read: () => void;
open: () => void;
download: () => void;
empty: (param: string) => void;
commoncart_clipboard_success: () => void;
commoncart_offline_clipboard_success: () => void;
};
fastlabel_response?: string; // Testo per clipboard Fastlabel
offline_response?: string; // Testo per clipboard Offline
}Flow Tipici β
Aggiunta Articolo β
1. Utente aggiunge articolo nell'iframe
2. Iframe chiama window.parent.cart.setCounter(newCount)
3. useTopbarCart aggiorna lo store Zustand
4. useCartSyncListener propaga l'update alle altre tab
5. Componente Cart si ri-renderizza con il nuovo counterApertura Carrello β
1. Utente clicca sul badge del carrello
2. Cart chiama iframe.contentWindow.common_cart.read()
3. Cart apre il dropdown
4. Utente clicca "Apri"
5. Cart chiama iframe.contentWindow.common_cart.open()
6. Iframe naviga alla pagina del carrelloSvuotamento Carrello β
1. Utente clicca "Svuota" nel dropdown
2. Cart chiama iframe.contentWindow.common_cart.empty("")
3. Iframe svuota il carrello e chiama window.parent.cart.setCounter(0)
4. useTopbarCart resetta lo store
5. useCartSyncListener sincronizza le altre tabTesting β
Il sistema include test completi per:
Store (tests/cart.store.test.ts) β
- Stato iniziale
setCounter,incrementCounter,decrementCounter- Gestione dropdown
- Reset
- Scenari realistici
Sincronizzazione (tests/useCartSyncListener.test.ts) β
- Sincronizzazione contatore
- Sincronizzazione
hasItems - Gestione cambiamenti rapidi
- Logging
Comunicazione (tests/useTopbarCart.test.ts) β
- Registrazione
window.cart - Chiamate verso iframe
- Gestione errori
- Cleanup
Componente (tests/Cart.test.tsx) β
- Rendering condizionale
- Interazioni utente
- Dropdown
- Azioni
- Click esterni
- Clipboard API
Debugging β
Il sistema include logging dettagliato:
// Store updates
[CartSyncListener] π€ Invio aggiornamento carrello ad altre tab: {...}
[CartSyncListener] π Ricevuto aggiornamento carrello da altra tab: {...}
// Hook communication
[useTopbarCart] π’ setCounter chiamata dall'iframe: 5
[useTopbarCart] π read chiamata: richiesta contenuto carrello
[useTopbarCart] π open chiamata: apertura carrello
[useTopbarCart] β¬οΈ download chiamata: download carrello
[useTopbarCart] ποΈ empty chiamata: svuotamento carrello
// Component actions
[Cart] β
Fastlabel text copiato nella clipboard
[Cart] β
Offline text copiato nella clipboard
[Cart] β Errore nella copia: ...Migrazione dal Legacy β
Prima (legacy Cart.js) β
// Class component con stato locale
// Nessuna sincronizzazione tra tab
// Uso di react-clipboard.js
// Dipendenza da jQuery/DOM direttoDopo (nuovo sistema) β
// Zustand store globale
// Sincronizzazione multi-tab automatica
// Clipboard API nativa
// React hooks moderni
// TypeScript con type safety
// Test coverage completoBest Practices β
Montare gli hook una sola volta:
useTopbarCarteuseCartSyncListenerdevono essere montati in un componente parent comune (es. AuthenticatedLayout)Non modificare lo store direttamente: Usare sempre i metodi forniti (
setCounter,incrementCounter, etc.)Gestire l'assenza dell'iframe: Il sistema gestisce gracefully l'assenza dell'iframe con warning nei log
Testare con piΓΉ tab: Aprire piΓΉ tab per verificare la sincronizzazione
Verificare le variabili globali: Assicurarsi che
window.commoncart_btn_offlineewindow.commoncart_btn_fastlabelsiano impostate correttamente
Troubleshooting β
Il contatore non si aggiorna β
- Verificare che
useTopbarCartsia montato - Controllare la console per errori
- Verificare che l'iframe chiami
window.parent.cart.setCounter()
Il dropdown non si apre β
- Verificare che il componente
<Cart />sia renderizzato - Controllare la console del browser per errori JavaScript
La sincronizzazione tra tab non funziona β
- Verificare che
useCartSyncListenersia montato - Controllare che BroadcastChannel API sia supportata
- Verificare che entrambe le tab siano sullo stesso dominio
Le azioni non funzionano β
- Verificare che l'iframe sia caricato (
document.getElementById('main-iframe')) - Controllare che
common_cartsia definito nell'iframe - Verificare i log della console per messaggi di errore
Performance β
- Bundle size: ~3KB (store + hooks + componente)
- Re-renders: Ottimizzati con selettori Zustand specifici
- Memory: Nessun memory leak grazie a cleanup nei
useEffect - Sincronizzazione: Debounced automaticamente da BroadcastChannel
CompatibilitΓ β
- React: 19+
- TypeScript: 5+
- Browser: Moderni con BroadcastChannel API
- Chrome/Edge: β
- Firefox: β
- Safari: β (15.4+)
- IE11: β (non supportato)