@magic-spells/cart-panel 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -42,44 +42,44 @@ Or include directly in your HTML:
42
42
  ```html
43
43
  <!-- Trigger button -->
44
44
  <button aria-haspopup="dialog" aria-controls="my-cart" aria-expanded="false">
45
- Open Cart (3 items)
45
+ Open Cart (3 items)
46
46
  </button>
47
47
 
48
48
  <!-- Cart modal dialog -->
49
49
  <cart-dialog id="my-cart" aria-labelledby="cart-title">
50
- <cart-panel>
51
- <div class="cart-header">
52
- <h2 id="cart-title">Shopping Cart</h2>
53
- <button data-action="hide-cart" aria-label="Close cart">&times;</button>
54
- </div>
55
-
56
- <div class="cart-body">
57
- <!-- Cart items using @magic-spells/cart-item -->
58
- <cart-item data-key="shopify-line-item-123">
59
- <cart-item-content>
60
- <div class="product-info">
61
- <img src="product.jpg" alt="Product" />
62
- <div>
63
- <h4>Awesome T-Shirt</h4>
64
- <div class="price">$29.99</div>
65
- </div>
66
- </div>
67
- <div class="quantity-controls">
68
- <input type="number" data-cart-quantity value="1" min="1" />
69
- <button data-action="remove">Remove</button>
70
- </div>
71
- </cart-item-content>
72
- <cart-item-processing>
73
- <div>Processing...</div>
74
- </cart-item-processing>
75
- </cart-item>
76
- </div>
77
-
78
- <div class="cart-footer">
79
- <div class="cart-total">Total: $29.99</div>
80
- <button class="checkout-btn">Checkout</button>
81
- </div>
82
- </cart-panel>
50
+ <cart-panel>
51
+ <div class="cart-header">
52
+ <h2 id="cart-title">Shopping Cart</h2>
53
+ <button data-action="hide-cart" aria-label="Close cart">&times;</button>
54
+ </div>
55
+
56
+ <div class="cart-body">
57
+ <!-- Cart items using @magic-spells/cart-item -->
58
+ <cart-item data-key="shopify-line-item-123">
59
+ <cart-item-content>
60
+ <div class="product-info">
61
+ <img src="product.jpg" alt="Product" />
62
+ <div>
63
+ <h4>Awesome T-Shirt</h4>
64
+ <div class="price">$29.99</div>
65
+ </div>
66
+ </div>
67
+ <div class="quantity-controls">
68
+ <input type="number" data-cart-quantity value="1" min="1" />
69
+ <button data-action="remove">Remove</button>
70
+ </div>
71
+ </cart-item-content>
72
+ <cart-item-processing>
73
+ <div>Processing...</div>
74
+ </cart-item-processing>
75
+ </cart-item>
76
+ </div>
77
+
78
+ <div class="cart-footer">
79
+ <div class="cart-total">Total: $29.99</div>
80
+ <button class="checkout-btn">Checkout</button>
81
+ </div>
82
+ </cart-panel>
83
83
  </cart-dialog>
84
84
  ```
85
85
 
@@ -98,6 +98,7 @@ The component automatically handles:
98
98
  - Fetching cart data from `/cart.json` on show
99
99
  - Updating cart items via `/cart/change.json` API calls
100
100
  - Managing cart item states and animations through integrated `@magic-spells/cart-item`
101
+ - Filtering out cart items with `_hidden` property from display and calculations
101
102
  - Emitting events for cart updates and state changes
102
103
 
103
104
  ## Configuration
@@ -132,28 +133,28 @@ Example:
132
133
  ```html
133
134
  <!-- Minimal cart modal -->
134
135
  <cart-dialog id="simple-cart">
135
- <cart-panel>
136
- <h2>Cart</h2>
137
- <button data-action="hide-cart">Close</button>
138
- <!-- Cart content here -->
139
- </cart-panel>
136
+ <cart-panel>
137
+ <h2>Cart</h2>
138
+ <button data-action="hide-cart">Close</button>
139
+ <!-- Cart content here -->
140
+ </cart-panel>
140
141
  </cart-dialog>
141
142
 
142
143
  <!-- Complete cart with all features -->
143
144
  <cart-dialog id="full-cart" aria-modal="true" aria-labelledby="cart-heading">
144
- <cart-overlay></cart-overlay>
145
- <cart-panel>
146
- <header class="cart-header">
147
- <h2 id="cart-heading">Shopping Cart</h2>
148
- <button data-action="hide-cart" aria-label="Close cart">×</button>
149
- </header>
150
- <div class="cart-content">
151
- <!-- Cart items will be rendered here -->
152
- </div>
153
- <footer class="cart-footer">
154
- <button class="checkout-btn">Checkout</button>
155
- </footer>
156
- </cart-panel>
145
+ <cart-overlay></cart-overlay>
146
+ <cart-panel>
147
+ <header class="cart-header">
148
+ <h2 id="cart-heading">Shopping Cart</h2>
149
+ <button data-action="hide-cart" aria-label="Close cart">×</button>
150
+ </header>
151
+ <div class="cart-content">
152
+ <!-- Cart items will be rendered here -->
153
+ </div>
154
+ <footer class="cart-footer">
155
+ <button class="checkout-btn">Checkout</button>
156
+ </footer>
157
+ </cart-panel>
157
158
  </cart-dialog>
158
159
  ```
159
160
 
@@ -166,52 +167,52 @@ The component provides complete styling control through CSS custom properties an
166
167
  ```css
167
168
  /* Customize modal positioning and sizing */
168
169
  cart-dialog {
169
- --cart-panel-width: min(500px, 95vw);
170
- --cart-panel-z-index: 9999;
171
- --cart-overlay-z-index: 9998;
170
+ --cart-panel-width: min(500px, 95vw);
171
+ --cart-panel-z-index: 9999;
172
+ --cart-overlay-z-index: 9998;
172
173
  }
173
174
 
174
175
  /* Customize overlay appearance */
175
176
  cart-overlay {
176
- --cart-overlay-background: rgba(0, 0, 0, 0.3);
177
- --cart-overlay-backdrop-filter: blur(8px);
177
+ --cart-overlay-background: rgba(0, 0, 0, 0.3);
178
+ --cart-overlay-backdrop-filter: blur(8px);
178
179
  }
179
180
 
180
181
  /* Customize panel styling */
181
182
  cart-panel {
182
- --cart-panel-background: #ffffff;
183
- --cart-panel-shadow: -10px 0 30px rgba(0, 0, 0, 0.2);
184
- --cart-panel-border-radius: 12px 0 0 12px;
183
+ --cart-panel-background: #ffffff;
184
+ --cart-panel-shadow: -10px 0 30px rgba(0, 0, 0, 0.2);
185
+ --cart-panel-border-radius: 12px 0 0 12px;
185
186
  }
186
187
 
187
188
  /* Customize animations */
188
189
  cart-dialog {
189
- --cart-transition-duration: 400ms;
190
- --cart-transition-timing: cubic-bezier(0.25, 0.8, 0.25, 1);
190
+ --cart-transition-duration: 400ms;
191
+ --cart-transition-timing: cubic-bezier(0.25, 0.8, 0.25, 1);
191
192
  }
192
193
 
193
194
  /* Style your cart content layout */
194
195
  cart-panel {
195
- display: flex;
196
- flex-direction: column;
196
+ display: flex;
197
+ flex-direction: column;
197
198
  }
198
199
 
199
200
  .cart-header {
200
- padding: 1.5rem;
201
- border-bottom: 1px solid #eee;
202
- background: #f8f9fa;
201
+ padding: 1.5rem;
202
+ border-bottom: 1px solid #eee;
203
+ background: #f8f9fa;
203
204
  }
204
205
 
205
206
  .cart-content {
206
- flex: 1;
207
- overflow-y: auto;
208
- padding: 1rem;
207
+ flex: 1;
208
+ overflow-y: auto;
209
+ padding: 1rem;
209
210
  }
210
211
 
211
212
  .cart-footer {
212
- padding: 1.5rem;
213
- border-top: 1px solid #eee;
214
- background: #f8f9fa;
213
+ padding: 1.5rem;
214
+ border-top: 1px solid #eee;
215
+ background: #f8f9fa;
215
216
  }
216
217
  ```
217
218
 
@@ -238,26 +239,26 @@ The component supports both CSS custom properties and SCSS variables for maximum
238
239
  ```css
239
240
  /* Dramatic slide-in effect */
240
241
  .dramatic-cart {
241
- --cart-transition-duration: 600ms;
242
- --cart-transition-timing: cubic-bezier(0.68, -0.55, 0.265, 1.55);
243
- --cart-overlay-background: rgba(0, 0, 0, 0.4);
244
- --cart-overlay-backdrop-filter: blur(10px);
242
+ --cart-transition-duration: 600ms;
243
+ --cart-transition-timing: cubic-bezier(0.68, -0.55, 0.265, 1.55);
244
+ --cart-overlay-background: rgba(0, 0, 0, 0.4);
245
+ --cart-overlay-backdrop-filter: blur(10px);
245
246
  }
246
247
 
247
248
  /* Subtle minimal styling */
248
249
  .minimal-cart {
249
- --cart-panel-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
250
- --cart-panel-border-radius: 8px;
251
- --cart-transition-duration: 200ms;
252
- --cart-overlay-background: rgba(0, 0, 0, 0.05);
250
+ --cart-panel-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
251
+ --cart-panel-border-radius: 8px;
252
+ --cart-transition-duration: 200ms;
253
+ --cart-overlay-background: rgba(0, 0, 0, 0.05);
253
254
  }
254
255
 
255
256
  /* Mobile-optimized full-width */
256
257
  @media (max-width: 768px) {
257
- .mobile-cart {
258
- --cart-panel-width: 100vw;
259
- --cart-panel-border-radius: 0;
260
- }
258
+ .mobile-cart {
259
+ --cart-panel-width: 100vw;
260
+ --cart-panel-border-radius: 0;
261
+ }
261
262
  }
262
263
  ```
263
264
 
@@ -276,8 +277,8 @@ $cart-overlay-background: rgba(0, 0, 0, 0.25);
276
277
  @import '@magic-spells/cart-panel/css';
277
278
 
278
279
  .my-store cart-dialog {
279
- --cart-transition-duration: 400ms;
280
- --cart-panel-background: #f8f9fa;
280
+ --cart-transition-duration: 400ms;
281
+ --cart-panel-background: #f8f9fa;
281
282
  }
282
283
  ```
283
284
 
@@ -330,32 +331,32 @@ await cartDialog.refreshCart();
330
331
 
331
332
  // Event emitter pattern (recommended)
332
333
  cartDialog
333
- .on('cart-dialog:show', (e) => {
334
- console.log('Cart opened by:', e.detail.triggerElement);
335
- })
336
- .on('cart-dialog:data-changed', (cartData) => {
337
- console.log('Cart updated:', cartData);
338
- // Update header cart count, etc.
339
- });
334
+ .on('cart-dialog:show', (e) => {
335
+ console.log('Cart opened by:', e.detail.triggerElement);
336
+ })
337
+ .on('cart-dialog:data-changed', (cartData) => {
338
+ console.log('Cart updated:', cartData);
339
+ // Update header cart count, etc.
340
+ });
340
341
 
341
342
  // Traditional event listeners (also supported)
342
343
  cartDialog.addEventListener('cart-item:remove', (e) => {
343
- console.log('Remove requested:', e.detail.cartKey);
344
+ console.log('Remove requested:', e.detail.cartKey);
344
345
 
345
- // The component handles the API calls automatically
346
- // Just listen for the data changes
346
+ // The component handles the API calls automatically
347
+ // Just listen for the data changes
347
348
  });
348
349
 
349
350
  cartDialog.addEventListener('cart-item:quantity-change', (e) => {
350
- console.log('Quantity changed:', e.detail.quantity);
351
- // Component automatically syncs with Shopify
351
+ console.log('Quantity changed:', e.detail.quantity);
352
+ // Component automatically syncs with Shopify
352
353
  });
353
354
 
354
355
  // Listen for all cart changes
355
356
  cartDialog.on('cart-dialog:data-changed', (cartData) => {
356
- // Update your UI when cart changes
357
- updateCartBadge(cartData.item_count);
358
- updateCartTotal(cartData.total_price);
357
+ // Update your UI when cart changes
358
+ updateCartBadge(cartData.item_count);
359
+ updateCartTotal(cartData.total_price);
359
360
  });
360
361
  ```
361
362
 
@@ -373,6 +374,196 @@ The component is optimized for:
373
374
 
374
375
  ## Integration Examples
375
376
 
377
+ ### Line Item Properties
378
+
379
+ The cart panel supports several Shopify line item properties for enhanced functionality:
380
+
381
+ #### Cart Item Filtering (`_hide_in_cart`)
382
+
383
+ Cart items can be hidden from display by setting the `_hide_in_cart` property. Hidden items are excluded from:
384
+
385
+ - Cart item display and rendering
386
+ - Cart count calculations
387
+ - Subtotal calculations
388
+
389
+ ```javascript
390
+ // Example: Hide a cart item from display
391
+ {
392
+ "items": [
393
+ {
394
+ "key": "item-123",
395
+ "properties": {
396
+ "_hide_in_cart": "true" // Hide from cart display
397
+ }
398
+ }
399
+ ]
400
+ }
401
+ ```
402
+
403
+ #### Custom Templates (`_cart_template`)
404
+
405
+ Different cart item templates can be specified using the `_cart_template` property:
406
+
407
+ ```javascript
408
+ // Example: Use different templates for different item types
409
+ {
410
+ "items": [
411
+ {
412
+ "key": "subscription-item",
413
+ "properties": {
414
+ "_cart_template": "subscription" // Use subscription template
415
+ }
416
+ },
417
+ {
418
+ "key": "bundle-item",
419
+ "properties": {
420
+ "_cart_template": "bundle" // Use bundle template
421
+ }
422
+ }
423
+ ]
424
+ }
425
+ ```
426
+
427
+ Then set up custom templates in JavaScript:
428
+
429
+ ```javascript
430
+ import { CartItem } from '@magic-spells/cart-panel';
431
+
432
+ // Set up different templates
433
+ CartItem.setTemplate('subscription', (itemData, cartData) => {
434
+ return `
435
+ <div class="subscription-item">
436
+ <div class="recurring-badge">🔄 Subscription</div>
437
+ <h4>${itemData.product_title}</h4>
438
+ <div class="price">$${(itemData.price / 100).toFixed(2)} every month</div>
439
+ <quantity-modifier value="${itemData.quantity}"></quantity-modifier>
440
+ </div>
441
+ `;
442
+ });
443
+
444
+ CartItem.setTemplate('bundle', (itemData, cartData) => {
445
+ return `
446
+ <div class="bundle-item">
447
+ <div class="bundle-badge">📦 Bundle Deal</div>
448
+ <h4>${itemData.product_title}</h4>
449
+ <div class="savings">Save 20%!</div>
450
+ <div class="price">$${(itemData.price / 100).toFixed(2)}</div>
451
+ </div>
452
+ `;
453
+ });
454
+ ```
455
+
456
+ #### Item Grouping (`_group_id` and `_group_role`)
457
+
458
+ Items can be grouped together using `_group_id` and `_group_role` properties. This is commonly used for bundle products where multiple items should be displayed as a single unit.
459
+
460
+ **Use Cases:**
461
+ - Bundle products (main product + accessories)
462
+ - Gift with purchase promotions
463
+ - Subscription boxes with multiple items
464
+ - Product kits and sets
465
+
466
+ **How it works:**
467
+ 1. All items in a group share the same `_group_id` (a unique identifier like a UUID)
468
+ 2. One item has `_group_role: "parent"` (typically with `_cart_template: "bundle"`)
469
+ 3. Other items have `_group_role: "child"` (typically with `_hide_in_cart: true`)
470
+ 4. The bundle template renders all grouped items together in one display
471
+
472
+ **Example usage:**
473
+ ```javascript
474
+ // Bundle: T-shirt + Hat + Sticker (shown as one item in cart)
475
+ {
476
+ "items": [
477
+ {
478
+ "key": "bundle-parent",
479
+ "properties": {
480
+ "_group_id": "Q6RT1B48",
481
+ "_group_role": "parent",
482
+ "_cart_template": "bundle"
483
+ }
484
+ },
485
+ {
486
+ "key": "bundle-child-1",
487
+ "properties": {
488
+ "_group_id": "Q6RT1B48",
489
+ "_group_role": "child",
490
+ "_hide_in_cart": "true"
491
+ }
492
+ },
493
+ {
494
+ "key": "bundle-child-2",
495
+ "properties": {
496
+ "_group_id": "Q6RT1B48",
497
+ "_group_role": "child",
498
+ "_hide_in_cart": "true"
499
+ }
500
+ }
501
+ ]
502
+ }
503
+ ```
504
+
505
+ **Bundle template example:**
506
+ ```javascript
507
+ CartItem.setTemplate('bundle', (itemData, cartData) => {
508
+ // Find all items in this group
509
+ const groupId = itemData.properties._group_id;
510
+ const groupItems = cartData.items.filter(item =>
511
+ item.properties?._group_id === groupId
512
+ );
513
+
514
+ return `
515
+ <div class="bundle-item">
516
+ <div class="bundle-badge">📦 Bundle Deal</div>
517
+ <h4>${itemData.product_title}</h4>
518
+ <div class="bundle-contents">
519
+ ${groupItems.map(item => `
520
+ <div class="bundle-item-detail">
521
+ • ${item.product_title} (${item.quantity})
522
+ </div>
523
+ `).join('')}
524
+ </div>
525
+ <div class="bundle-price">$${(groupItems.reduce((sum, item) => sum + item.line_price, 0) / 100).toFixed(2)}</div>
526
+ </div>
527
+ `;
528
+ });
529
+ ```
530
+
531
+ #### Subtotal Exclusion (`_ignore_price_in_subtotal`)
532
+
533
+ Items can be excluded from subtotal calculations using the `_ignore_price_in_subtotal` property. This is useful for promotional items that receive automatic discounts at checkout.
534
+
535
+ **Use Cases:**
536
+ - Gift with purchase items (free items that show $0 at checkout)
537
+ - Promotional items with automatic discounts applied later
538
+ - Service fees handled by other systems
539
+ - Items with complex pricing logic
540
+
541
+ **Usage:**
542
+ ```javascript
543
+ // Gift with purchase item - shows in cart but excluded from subtotal
544
+ {
545
+ "key": "gift-item",
546
+ "properties": {
547
+ "_ignore_price_in_subtotal": "true"
548
+ }
549
+ }
550
+ ```
551
+
552
+ **Implementation:**
553
+ The cart panel automatically excludes these items when calculating visible subtotals, but they remain in the cart for Shopify's checkout process where discounts are applied.
554
+
555
+ #### Supported Properties
556
+
557
+ | Property | Purpose | Example Values |
558
+ | -------------------------- | --------------------------------------------- | -------------------------------------- |
559
+ | `_hide_in_cart` | Hide items from cart display | `"true"`, `true` |
560
+ | `_cart_template` | Specify custom template for rendering | `"subscription"`, `"bundle"`, `"gift"` |
561
+ | `_group_id` | Group items together with shared UUID | `"Q6RT1B48"`, `"ABC123XYZ"` |
562
+ | `_group_role` | Role within a group | `"parent"`, `"child"` |
563
+ | `_ignore_price_in_subtotal` | Exclude from subtotal calculations | `"true"`, `true` |
564
+
565
+ These properties follow Shopify's line item properties pattern and are commonly used for gift-with-purchase items, subscription products, bundles, and other special cart items.
566
+
376
567
  ### Shopify Integration
377
568
 
378
569
  The cart panel automatically integrates with Shopify's AJAX Cart API. Simply add the component to your theme and it handles all cart operations:
@@ -380,43 +571,43 @@ The cart panel automatically integrates with Shopify's AJAX Cart API. Simply add
380
571
  ```html
381
572
  <!-- In your Shopify theme layout -->
382
573
  <button
383
- aria-haspopup="dialog"
384
- aria-controls="shopify-cart"
385
- aria-expanded="false"
386
- class="cart-trigger">
387
- Cart ({{ cart.item_count }})
574
+ aria-haspopup="dialog"
575
+ aria-controls="shopify-cart"
576
+ aria-expanded="false"
577
+ class="cart-trigger">
578
+ Cart ({{ cart.item_count }})
388
579
  </button>
389
580
 
390
581
  <cart-dialog id="shopify-cart" aria-labelledby="cart-heading">
391
- <cart-panel>
392
- <header class="cart-header">
393
- <h2 id="cart-heading">Your Cart</h2>
394
- <button data-action="hide-cart" aria-label="hide cart">X</button>
395
- </header>
396
-
397
- <div class="cart-content">
398
- <!-- Cart items will be populated automatically in javascript -->
399
- </div>
400
-
401
- <footer class="cart-footer">
402
- <div class="cart-total"></div>
403
- <a href="/checkout" class="button"> Checkout </a>
404
- </footer>
405
- </cart-panel>
582
+ <cart-panel>
583
+ <header class="cart-header">
584
+ <h2 id="cart-heading">Your Cart</h2>
585
+ <button data-action="hide-cart" aria-label="hide cart">X</button>
586
+ </header>
587
+
588
+ <div class="cart-content">
589
+ <!-- Cart items will be populated automatically in javascript -->
590
+ </div>
591
+
592
+ <footer class="cart-footer">
593
+ <div class="cart-total"></div>
594
+ <a href="/checkout" class="button"> Checkout </a>
595
+ </footer>
596
+ </cart-panel>
406
597
  </cart-dialog>
407
598
 
408
599
  <script>
409
- // Optional: Listen for cart updates to sync with other UI elements
410
- document.querySelector('cart-dialog').on('cart-dialog:data-changed', (cartData) => {
411
- // Update cart count in header
412
- document.querySelector('.cart-trigger').textContent = `Cart (${cartData.item_count})`;
413
-
414
- // Update cart total
415
- document.querySelector('[data-cart-total]').textContent = new Intl.NumberFormat('en-US', {
416
- style: 'currency',
417
- currency: 'USD',
418
- }).format(cartData.total_price / 100);
419
- });
600
+ // Optional: Listen for cart updates to sync with other UI elements
601
+ document.querySelector('cart-dialog').on('cart-dialog:data-changed', (cartData) => {
602
+ // Update cart count in header
603
+ document.querySelector('.cart-trigger').textContent = `Cart (${cartData.item_count})`;
604
+
605
+ // Update cart total
606
+ document.querySelector('[data-cart-total]').textContent = new Intl.NumberFormat('en-US', {
607
+ style: 'currency',
608
+ currency: 'USD',
609
+ }).format(cartData.total_price / 100);
610
+ });
420
611
  </script>
421
612
  ```
422
613
 
@@ -425,63 +616,63 @@ The cart panel automatically integrates with Shopify's AJAX Cart API. Simply add
425
616
  ```javascript
426
617
  // Example for non-Shopify platforms
427
618
  class CustomCartManager {
428
- constructor() {
429
- this.cartDialog = document.querySelector('cart-dialog');
430
- this.setupEventListeners();
431
- }
432
-
433
- setupEventListeners() {
434
- // Listen for cart data changes
435
- this.cartDialog.on('cart-dialog:data-changed', (cartData) => {
436
- this.updateCartUI(cartData);
437
- });
438
-
439
- // Override default cart operations for custom API
440
- this.cartDialog.getCart = this.customGetCart.bind(this);
441
- this.cartDialog.updateCartItem = this.customUpdateCartItem.bind(this);
442
- }
443
-
444
- async customGetCart() {
445
- try {
446
- const response = await fetch('/api/cart');
447
- return await response.json();
448
- } catch (error) {
449
- console.error('Failed to fetch cart:', error);
450
- return { error: true, message: error.message };
451
- }
452
- }
453
-
454
- async customUpdateCartItem(itemId, quantity) {
455
- try {
456
- const response = await fetch('/api/cart/update', {
457
- method: 'POST',
458
- headers: { 'Content-Type': 'application/json' },
459
- body: JSON.stringify({ itemId, quantity }),
460
- });
461
-
462
- if (!response.ok) throw new Error(response.statusText);
463
-
464
- // Return updated cart data
465
- return this.customGetCart();
466
- } catch (error) {
467
- console.error('Failed to update cart:', error);
468
- return { error: true, message: error.message };
469
- }
470
- }
471
-
472
- updateCartUI(cartData) {
473
- // Update cart count in navigation
474
- const cartCount = document.querySelector('.cart-count');
475
- if (cartCount) {
476
- cartCount.textContent = cartData.items?.length || 0;
477
- }
478
-
479
- // Update cart total display
480
- const cartTotal = document.querySelector('.cart-total-display');
481
- if (cartTotal && cartData.total) {
482
- cartTotal.textContent = cartData.total;
483
- }
484
- }
619
+ constructor() {
620
+ this.cartDialog = document.querySelector('cart-dialog');
621
+ this.setupEventListeners();
622
+ }
623
+
624
+ setupEventListeners() {
625
+ // Listen for cart data changes
626
+ this.cartDialog.on('cart-dialog:data-changed', (cartData) => {
627
+ this.updateCartUI(cartData);
628
+ });
629
+
630
+ // Override default cart operations for custom API
631
+ this.cartDialog.getCart = this.customGetCart.bind(this);
632
+ this.cartDialog.updateCartItem = this.customUpdateCartItem.bind(this);
633
+ }
634
+
635
+ async customGetCart() {
636
+ try {
637
+ const response = await fetch('/api/cart');
638
+ return await response.json();
639
+ } catch (error) {
640
+ console.error('Failed to fetch cart:', error);
641
+ return { error: true, message: error.message };
642
+ }
643
+ }
644
+
645
+ async customUpdateCartItem(itemId, quantity) {
646
+ try {
647
+ const response = await fetch('/api/cart/update', {
648
+ method: 'POST',
649
+ headers: { 'Content-Type': 'application/json' },
650
+ body: JSON.stringify({ itemId, quantity }),
651
+ });
652
+
653
+ if (!response.ok) throw new Error(response.statusText);
654
+
655
+ // Return updated cart data
656
+ return this.customGetCart();
657
+ } catch (error) {
658
+ console.error('Failed to update cart:', error);
659
+ return { error: true, message: error.message };
660
+ }
661
+ }
662
+
663
+ updateCartUI(cartData) {
664
+ // Update cart count in navigation
665
+ const cartCount = document.querySelector('.cart-count');
666
+ if (cartCount) {
667
+ cartCount.textContent = cartData.items?.length || 0;
668
+ }
669
+
670
+ // Update cart total display
671
+ const cartTotal = document.querySelector('.cart-total-display');
672
+ if (cartTotal && cartData.total) {
673
+ cartTotal.textContent = cartData.total;
674
+ }
675
+ }
485
676
  }
486
677
 
487
678
  // Initialize