@dutchiesdk/ecommerce-extensions-sdk 0.6.3 → 0.7.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
@@ -1,37 +1,18 @@
1
-
2
-
3
1
  ![Dutchie Logo](https://cdn.prod.website-files.com/61930755e474060ca6298871/64a32a14b7840d452f594fbc_Logo.svg)
4
2
 
5
- # @dutchiesdk/ecommerce-extensions-sdk
3
+ # @dutchiesdk/ecommerce-extensions-sdk
6
4
 
7
- A comprehensive TypeScript SDK for building custom extensions that run on the Dutchie Ecommerce Pro platform. This SDK provides certified agency partners with the tools needed to create powerful, integrated e-commerce experiences for cannabis retailers.
5
+ This SDK provides certified agency partners with the tools needed to create powerful, integrated e-commerce experiences for cannabis retailers on the Dutchie Ecommerce Pro platform.
8
6
 
9
7
  > ⚠️ **Alpha Release Warning**
10
- >
8
+ >
11
9
  > This SDK is currently in **alpha** and is subject to breaking changes. APIs, types, and functionality may change significantly between versions. Please use with caution in production environments and be prepared to update your extensions as the SDK evolves.
12
10
 
13
11
  ## Development Environment Access
14
12
 
15
13
  This SDK requires a **Dutchie-provided development environment** to build, test, and deploy extensions. The SDK alone is not sufficient for development - you must have access to the complete Dutchie Pro development and deployment infrastructure.
16
14
 
17
- **To request access to the development environment:**
18
- 📧 Contact: **partners@dutchie.com**
19
-
20
- Please include your agency information and intended use case when requesting access.
21
-
22
- ## Table of Contents
23
-
24
- - [Installation](#installation)
25
- - [Quick Start](#quick-start)
26
- - [Core Concepts](#core-concepts)
27
- - [React Context & Hooks](#react-context--hooks)
28
- - [Data Types Reference](#data-types-reference)
29
- - [Extension Components](#extension-components)
30
- - [Actions API](#actions-api)
31
- - [Data Loaders](#data-loaders)
32
- - [Examples](#examples)
33
- - [Best Practices](#best-practices)
34
- - [Development](#development)
15
+ To request access to the development environment contact: **partners@dutchie.com**. Please include your agency information and intended use case when requesting access.
35
16
 
36
17
  ## Installation
37
18
 
@@ -39,19 +20,21 @@ Please include your agency information and intended use case when requesting acc
39
20
  npm install @dutchie/ecommerce-extensions-sdk
40
21
  # or
41
22
  yarn add @dutchie/ecommerce-extensions-sdk
42
- # or
43
- pnpm add @dutchie/ecommerce-extensions-sdk
44
23
  ```
45
24
 
46
25
  ## Quick Start
47
26
 
48
27
  ```tsx
49
- import React from 'react';
50
- import { useDataBridge, RemoteBoundaryComponent } from '@dutchie/ecommerce-extensions-sdk';
28
+ import React from "react";
29
+ import {
30
+ useDataBridge,
31
+ RemoteBoundaryComponent,
32
+ DataBridgeVersion,
33
+ } from "@dutchie/ecommerce-extensions-sdk";
51
34
 
52
35
  const MyExtension: RemoteBoundaryComponent = () => {
53
36
  const { dataLoaders, actions, location, user, cart } = useDataBridge();
54
-
37
+
55
38
  return (
56
39
  <div>
57
40
  <h1>Welcome to {location?.name}</h1>
@@ -60,20 +43,11 @@ const MyExtension: RemoteBoundaryComponent = () => {
60
43
  );
61
44
  };
62
45
 
63
- MyExtension.DataBridgeVersion = '0.2';
46
+ MyExtension.DataBridgeVersion = DataBridgeVersion;
64
47
 
65
48
  export default MyExtension;
66
49
  ```
67
50
 
68
- ## Core Concepts
69
-
70
- The Dutchie Ecommerce Extensions SDK is built around several key concepts:
71
-
72
- 1. **Data Bridge**: A unified interface for accessing dispensary data, user information, and cart state
73
- 2. **Actions**: Pre-built navigation and cart management functions
74
- 3. **Remote Components**: Extension components that integrate seamlessly with the Dutchie platform
75
- 4. **Context-Driven Architecture**: React context provides data and actions throughout your extension
76
-
77
51
  ## React Context & Hooks
78
52
 
79
53
  ### `useDataBridge()`
@@ -81,18 +55,18 @@ The Dutchie Ecommerce Extensions SDK is built around several key concepts:
81
55
  The primary hook for accessing the Dutchie platform data and functionality.
82
56
 
83
57
  ```tsx
84
- import { useDataBridge } from '@dutchie/ecommerce-extensions-sdk';
58
+ import { useDataBridge } from "@dutchie/ecommerce-extensions-sdk";
85
59
 
86
60
  const MyComponent = () => {
87
61
  const {
88
- menuContext, // 'store-front' | 'kiosk'
89
- location, // Current dispensary information
90
- user, // Authenticated user data
91
- cart, // Current cart state
92
- dataLoaders, // Async data loading functions
93
- actions // Navigation and cart actions
62
+ menuContext, // 'store-front' | 'kiosk'
63
+ location, // Current dispensary information
64
+ user, // Authenticated user data
65
+ cart, // Current cart state
66
+ dataLoaders, // Async data loading functions
67
+ actions, // Navigation and cart actions
94
68
  } = useDataBridge();
95
-
69
+
96
70
  // Component logic here
97
71
  };
98
72
  ```
@@ -104,17 +78,20 @@ const MyComponent = () => {
104
78
  A utility hook for handling async data loading with loading states.
105
79
 
106
80
  ```tsx
107
- import { useAsyncLoader, useDataBridge } from '@dutchie/ecommerce-extensions-sdk';
81
+ import {
82
+ useAsyncLoader,
83
+ useDataBridge,
84
+ } from "@dutchie/ecommerce-extensions-sdk";
108
85
 
109
86
  const ProductList = () => {
110
87
  const { dataLoaders } = useDataBridge();
111
88
  const { data: products, isLoading } = useAsyncLoader(dataLoaders.products);
112
-
89
+
113
90
  if (isLoading) return <div>Loading products...</div>;
114
-
91
+
115
92
  return (
116
93
  <div>
117
- {products?.map(product => (
94
+ {products?.map((product) => (
118
95
  <div key={product.id}>{product.name}</div>
119
96
  ))}
120
97
  </div>
@@ -122,24 +99,15 @@ const ProductList = () => {
122
99
  };
123
100
  ```
124
101
 
125
- **Parameters:**
126
- - `fn`: `() => Promise<unknown>` - Async function to execute
102
+ ## Data Types
127
103
 
128
- **Returns:**
129
- - `data`: The resolved data or `null` while loading
130
- - `isLoading`: Boolean indicating loading state
131
-
132
- ## Data Types Reference
133
-
134
- ### Core Platform Types
135
-
136
- #### `CommerceComponentsDataInterface`
104
+ ### `CommerceComponentsDataInterface`
137
105
 
138
106
  The main interface providing access to all platform data and functionality.
139
107
 
140
108
  ```typescript
141
109
  interface CommerceComponentsDataInterface {
142
- menuContext: 'store-front' | 'kiosk';
110
+ menuContext: "store-front" | "kiosk";
143
111
  location?: Dispensary;
144
112
  user?: User;
145
113
  cart?: Cart;
@@ -148,159 +116,6 @@ interface CommerceComponentsDataInterface {
148
116
  }
149
117
  ```
150
118
 
151
- #### `Dispensary`
152
-
153
- Complete dispensary information including location, hours, and capabilities.
154
-
155
- ```typescript
156
- interface Dispensary {
157
- id: string;
158
- status: string;
159
- name: string;
160
- cname: string; // URL-friendly name
161
- chain: string;
162
- phone: string;
163
- email: string;
164
- hours: DispensaryHoursSettings;
165
- orderTypes: DispensaryOrderTypes;
166
- images: {
167
- logo: string;
168
- };
169
- address: DispensaryAddress;
170
- links: {
171
- website: string;
172
- storeFrontRoot: string;
173
- };
174
- orderTypesConfig: OrderTypesConfigV2;
175
- }
176
- ```
177
-
178
- #### `Product`
179
-
180
- Product information with essential e-commerce data.
181
-
182
- ```typescript
183
- interface Product {
184
- id: string;
185
- name: string;
186
- cname: string; // URL-friendly name
187
- price: number;
188
- image: string;
189
- description: string;
190
- }
191
- ```
192
-
193
- #### `User`
194
-
195
- Authenticated user information.
196
-
197
- ```typescript
198
- interface User {
199
- email: string;
200
- firstName: string;
201
- lastName: string;
202
- birthday: string;
203
- }
204
- ```
205
-
206
- #### `Cart`
207
-
208
- Current shopping cart state and totals.
209
-
210
- ```typescript
211
- interface Cart {
212
- items: CartItem[];
213
- total: number;
214
- subtotal: number;
215
- tax: number;
216
- discount: number;
217
- }
218
-
219
- interface CartItem {
220
- productId: string;
221
- name: string;
222
- price: number;
223
- quantity: number;
224
- }
225
- ```
226
-
227
- ### Category & Brand Types
228
-
229
- #### `Category`
230
-
231
- ```typescript
232
- interface Category {
233
- id: string;
234
- name: string;
235
- cname: string;
236
- }
237
- ```
238
-
239
- #### `Brand`
240
-
241
- ```typescript
242
- interface Brand {
243
- id: string;
244
- name: string;
245
- cname: string;
246
- }
247
- ```
248
-
249
- #### `Collection`
250
-
251
- ```typescript
252
- interface Collection {
253
- id: string;
254
- name: string;
255
- cname: string;
256
- }
257
- ```
258
-
259
- ### Business Hours & Order Types
260
-
261
- #### `DispensaryHoursSettings`
262
-
263
- ```typescript
264
- interface DispensaryHoursSettings {
265
- inStorePickup?: HoursSettingsForOrderType;
266
- curbsidePickup?: HoursSettingsForOrderType;
267
- driveThruPickup?: HoursSettingsForOrderType;
268
- delivery?: HoursSettingsForOrderType;
269
- }
270
-
271
- interface HoursSettingsForOrderType {
272
- enabled: boolean;
273
- effectiveHours?: {
274
- Monday?: DayHours;
275
- Tuesday?: DayHours;
276
- Wednesday?: DayHours;
277
- Thursday?: DayHours;
278
- Friday?: DayHours;
279
- Saturday?: DayHours;
280
- Sunday?: DayHours;
281
- };
282
- }
283
-
284
- interface DayHours {
285
- active?: boolean;
286
- start?: string; // "09:00"
287
- end?: string; // "21:00"
288
- }
289
- ```
290
-
291
- #### `DispensaryOrderTypes`
292
-
293
- ```typescript
294
- interface DispensaryOrderTypes {
295
- pickup: boolean;
296
- inStorePickup: boolean;
297
- curbsidePickup: boolean;
298
- driveThruPickup: boolean;
299
- delivery: boolean;
300
- kiosk: boolean;
301
- }
302
- ```
303
-
304
119
  ## Extension Components
305
120
 
306
121
  ### `RemoteBoundaryComponent`
@@ -318,7 +133,7 @@ type RemoteBoundaryComponent = React.FC & {
318
133
  ```tsx
319
134
  const MyCustomHeader: RemoteBoundaryComponent = () => {
320
135
  const { location, user, actions } = useDataBridge();
321
-
136
+
322
137
  return (
323
138
  <header>
324
139
  <h1>{location?.name}</h1>
@@ -331,75 +146,31 @@ const MyCustomHeader: RemoteBoundaryComponent = () => {
331
146
  );
332
147
  };
333
148
 
334
- MyCustomHeader.DataBridgeVersion = '0.2';
335
- ```
336
-
337
- ### `RemoteModuleRegistry`
338
-
339
- Registry for different types of extension components.
340
-
341
- ```typescript
342
- interface RemoteModuleRegistry {
343
- RouteablePages?: RoutablePageRegistryEntry[];
344
- StoreFrontHeader?: RemoteBoundaryComponent;
345
- StoreFrontNavigation?: RemoteBoundaryComponent;
346
- StoreFrontFooter?: RemoteBoundaryComponent;
347
- StoreFrontHero?: RemoteBoundaryComponent;
348
- ProductDetailsPrimary?: RemoteBoundaryComponent;
349
- ProductDetailsSecondary?: RemoteBoundaryComponent;
350
- }
351
-
352
- interface RoutablePageRegistryEntry {
353
- path: string;
354
- component: RemoteBoundaryComponent;
355
- }
149
+ MyCustomHeader.DataBridgeVersion = "0.2";
356
150
  ```
357
151
 
358
152
  ## Actions API
359
153
 
360
154
  The actions object provides pre-built functions for common e-commerce operations.
361
155
 
362
- ### Navigation Actions
156
+ It can be used to route:
363
157
 
364
158
  ```typescript
365
159
  const { actions } = useDataBridge();
366
160
 
367
- // Page navigation
368
- actions.goToInfoPage(); // Navigate to store info
369
- actions.goToStoreFront(); // Navigate to store front
370
- actions.goToStoreLocator(); // Navigate to store locator
371
- actions.goToStoreBrowser(); // Navigate to store browser
372
-
373
- // Authentication
374
- actions.goToLogin(); // Navigate to login page
375
- actions.goToRegister(); // Navigate to registration
161
+ actions.goToLogin(); // Navigate to login page
376
162
 
377
163
  // Product & category navigation
378
- actions.goToProductDetails('product-123'); // Navigate to product details
379
- actions.goToCategory('flower'); // Navigate to category page
380
- actions.goToBrand('brand-456'); // Navigate to brand page
381
- actions.goToStore('store-789', 'dispensary-name'); // Navigate to specific store
382
- actions.goToProductList('category-id', 'category-name'); // Navigate to product list
383
- actions.goToBrandList('brand-id', 'brand-name'); // Navigate to brand list
384
-
385
- // Checkout
386
- actions.goToCheckout(); // Navigate to checkout
164
+ actions.goToProductDetails("product-123"); // Navigate to product details
387
165
  ```
388
166
 
389
- ### Cart Actions
167
+ Or to update state:
390
168
 
391
169
  ```typescript
392
170
  const { actions } = useDataBridge();
393
171
 
394
- // Cart visibility
395
- actions.showCart(); // Show cart sidebar/modal
396
- actions.hideCart(); // Hide cart sidebar/modal
397
-
398
- // Cart management
399
- actions.addToCart('product-123', 2); // Add 2 items to cart
400
- actions.removeFromCart('product-123'); // Remove product from cart
401
- actions.updateCartItem('product-123', 5); // Update quantity to 5
402
- actions.clearCart(); // Remove all items from cart
172
+ actions.addToCart("product-123", 2); // Add 2 items to cart
173
+ actions.clearCart(); // Remove all items from cart
403
174
  ```
404
175
 
405
176
  ## Data Loaders
@@ -410,293 +181,15 @@ Async functions for loading platform data. All loaders return promises that reso
410
181
  const { dataLoaders } = useDataBridge();
411
182
 
412
183
  // Location data
413
- const locations = await dataLoaders.getAllLocations(); // All dispensary locations
414
- const currentLocations = await dataLoaders.locations(); // Current context locations
415
-
416
- // Catalog data
417
- const categories = await dataLoaders.categories(); // Product categories
418
- const brands = await dataLoaders.brands(); // Available brands
419
- const products = await dataLoaders.products(); // Product catalog
420
- const collections = await dataLoaders.collections(); // Product collections
184
+ const locations = await dataLoaders.getAllLocations(); // All dispensary locations
185
+ const currentLocations = await dataLoaders.locations(); // Current context locations
421
186
  ```
422
187
 
423
188
  **Note:** All data loaders return empty arrays (`[]`) when no data is available.
424
189
 
425
- ## Examples
426
-
427
- ### Complete Product Catalog Component
428
-
429
- ```tsx
430
- import React from 'react';
431
- import { useDataBridge, useAsyncLoader, RemoteBoundaryComponent } from '@dutchie/ecommerce-extensions-sdk';
432
-
433
- const ProductCatalog: RemoteBoundaryComponent = () => {
434
- const { dataLoaders, actions, cart } = useDataBridge();
435
-
436
- const { data: products, isLoading: productsLoading } = useAsyncLoader(dataLoaders.products);
437
- const { data: categories, isLoading: categoriesLoading } = useAsyncLoader(dataLoaders.categories);
438
-
439
- const [selectedCategory, setSelectedCategory] = React.useState<string>('');
440
-
441
- const filteredProducts = selectedCategory
442
- ? products?.filter(product => /* filter logic */)
443
- : products || [];
444
-
445
- if (productsLoading || categoriesLoading) {
446
- return <div className="loading">Loading catalog...</div>;
447
- }
448
-
449
- return (
450
- <div className="product-catalog">
451
- <div className="filters">
452
- <select
453
- value={selectedCategory}
454
- onChange={(e) => setSelectedCategory(e.target.value)}
455
- >
456
- <option value="">All Categories</option>
457
- {categories?.map(category => (
458
- <option key={category.id} value={category.id}>
459
- {category.name}
460
- </option>
461
- ))}
462
- </select>
463
- </div>
464
-
465
- <div className="product-grid">
466
- {filteredProducts.map(product => (
467
- <div key={product.id} className="product-card">
468
- <img src={product.image} alt={product.name} />
469
- <h3>{product.name}</h3>
470
- <p>${product.price.toFixed(2)}</p>
471
- <p>{product.description}</p>
472
-
473
- <div className="product-actions">
474
- <button
475
- onClick={() => actions.goToProductDetails(product.id)}
476
- >
477
- View Details
478
- </button>
479
-
480
- <button
481
- onClick={() => actions.addToCart(product.id, 1)}
482
- >
483
- Add to Cart
484
- </button>
485
- </div>
486
- </div>
487
- ))}
488
- </div>
489
-
490
- {cart && cart.items.length > 0 && (
491
- <div className="cart-summary">
492
- <p>Cart: {cart.items.length} items - ${cart.total.toFixed(2)}</p>
493
- <button onClick={actions.showCart}>View Cart</button>
494
- </div>
495
- )}
496
- </div>
497
- );
498
- };
499
-
500
- ProductCatalog.DataBridgeVersion = '0.2';
501
- export default ProductCatalog;
502
- ```
503
-
504
- ### Custom Store Locator
505
-
506
- ```tsx
507
- import React from 'react';
508
- import { useDataBridge, useAsyncLoader, RemoteBoundaryComponent } from '@dutchie/ecommerce-extensions-sdk';
509
-
510
- const StoreLocator: RemoteBoundaryComponent = () => {
511
- const { dataLoaders, actions, location: currentLocation } = useDataBridge();
512
- const { data: locations, isLoading } = useAsyncLoader(dataLoaders.getAllLocations);
513
-
514
- if (isLoading) return <div>Loading locations...</div>;
515
-
516
- const formatHours = (hours: any) => {
517
- if (!hours?.effectiveHours) return 'Hours not available';
518
-
519
- const today = new Date().toLocaleDateString('en-US', { weekday: 'long' });
520
- const todayHours = hours.effectiveHours[today];
521
-
522
- if (!todayHours?.active) return 'Closed today';
523
- return `${todayHours.start} - ${todayHours.end}`;
524
- };
525
-
526
- return (
527
- <div className="store-locator">
528
- <h2>Find a Store</h2>
529
-
530
- {locations?.map(store => (
531
- <div
532
- key={store.id}
533
- className={`store-card ${store.id === currentLocation?.id ? 'current' : ''}`}
534
- >
535
- <div className="store-header">
536
- <img src={store.images.logo} alt={`${store.name} logo`} />
537
- <div>
538
- <h3>{store.name}</h3>
539
- <p className="chain">{store.chain}</p>
540
- </div>
541
- </div>
542
-
543
- <div className="store-details">
544
- <div className="address">
545
- <p>{store.address.street1}</p>
546
- {store.address.street2 && <p>{store.address.street2}</p>}
547
- <p>{store.address.city}, {store.address.stateAbbreviation} {store.address.zip}</p>
548
- </div>
549
-
550
- <div className="contact">
551
- <p>📞 {store.phone}</p>
552
- <p>✉️ {store.email}</p>
553
- </div>
554
-
555
- <div className="hours">
556
- <p><strong>Today:</strong> {formatHours(store.hours.inStorePickup)}</p>
557
- </div>
558
-
559
- <div className="services">
560
- {store.orderTypes.inStorePickup && <span className="service">In-Store Pickup</span>}
561
- {store.orderTypes.curbsidePickup && <span className="service">Curbside</span>}
562
- {store.orderTypes.delivery && <span className="service">Delivery</span>}
563
- </div>
564
- </div>
565
-
566
- <div className="store-actions">
567
- <button onClick={() => actions.goToStore(store.id, store.cname)}>
568
- Visit Store
569
- </button>
570
- {store.links.website && (
571
- <a href={store.links.website} target="_blank" rel="noopener noreferrer">
572
- Website
573
- </a>
574
- )}
575
- </div>
576
- </div>
577
- ))}
578
- </div>
579
- );
580
- };
581
-
582
- StoreLocator.DataBridgeVersion = '0.2';
583
- export default StoreLocator;
584
- ```
585
-
586
- ### Enhanced Cart Component
587
-
588
- ```tsx
589
- import React from 'react';
590
- import { useDataBridge, RemoteBoundaryComponent } from '@dutchie/ecommerce-extensions-sdk';
591
-
592
- const EnhancedCart: RemoteBoundaryComponent = () => {
593
- const { cart, actions, location } = useDataBridge();
594
-
595
- if (!cart || cart.items.length === 0) {
596
- return (
597
- <div className="empty-cart">
598
- <h3>Your cart is empty</h3>
599
- <button onClick={actions.goToStoreFront}>
600
- Continue Shopping
601
- </button>
602
- </div>
603
- );
604
- }
605
-
606
- return (
607
- <div className="enhanced-cart">
608
- <div className="cart-header">
609
- <h2>Your Cart</h2>
610
- <p>Order from {location?.name}</p>
611
- </div>
612
-
613
- <div className="cart-items">
614
- {cart.items.map(item => (
615
- <div key={item.productId} className="cart-item">
616
- <div className="item-details">
617
- <h4>{item.name}</h4>
618
- <p className="price">${item.price.toFixed(2)} each</p>
619
- </div>
620
-
621
- <div className="quantity-controls">
622
- <button
623
- onClick={() => actions.updateCartItem(item.productId, item.quantity - 1)}
624
- disabled={item.quantity <= 1}
625
- >
626
- -
627
- </button>
628
- <span className="quantity">{item.quantity}</span>
629
- <button
630
- onClick={() => actions.updateCartItem(item.productId, item.quantity + 1)}
631
- >
632
- +
633
- </button>
634
- </div>
635
-
636
- <div className="item-total">
637
- ${(item.price * item.quantity).toFixed(2)}
638
- </div>
639
-
640
- <button
641
- className="remove-item"
642
- onClick={() => actions.removeFromCart(item.productId)}
643
- >
644
- Remove
645
- </button>
646
- </div>
647
- ))}
648
- </div>
649
-
650
- <div className="cart-summary">
651
- <div className="summary-line">
652
- <span>Subtotal:</span>
653
- <span>${cart.subtotal.toFixed(2)}</span>
654
- </div>
655
-
656
- {cart.discount > 0 && (
657
- <div className="summary-line discount">
658
- <span>Discount:</span>
659
- <span>-${cart.discount.toFixed(2)}</span>
660
- </div>
661
- )}
662
-
663
- <div className="summary-line">
664
- <span>Tax:</span>
665
- <span>${cart.tax.toFixed(2)}</span>
666
- </div>
667
-
668
- <div className="summary-line total">
669
- <span><strong>Total:</strong></span>
670
- <span><strong>${cart.total.toFixed(2)}</strong></span>
671
- </div>
672
- </div>
673
-
674
- <div className="cart-actions">
675
- <button
676
- className="clear-cart"
677
- onClick={actions.clearCart}
678
- >
679
- Clear Cart
680
- </button>
681
-
682
- <button
683
- className="checkout-btn"
684
- onClick={actions.goToCheckout}
685
- >
686
- Proceed to Checkout
687
- </button>
688
- </div>
689
- </div>
690
- );
691
- };
692
-
693
- EnhancedCart.DataBridgeVersion = '0.2';
694
- export default EnhancedCart;
695
- ```
696
-
697
190
  ## Best Practices
698
191
 
699
- ### 1. Error Handling
192
+ ### Error Handling
700
193
 
701
194
  Always wrap `useDataBridge()` calls in error boundaries and handle loading states:
702
195
 
@@ -705,10 +198,10 @@ const SafeComponent = () => {
705
198
  try {
706
199
  const { dataLoaders } = useDataBridge();
707
200
  const { data, isLoading } = useAsyncLoader(dataLoaders.products);
708
-
201
+
709
202
  if (isLoading) return <LoadingSpinner />;
710
203
  if (!data) return <ErrorMessage message="Failed to load products" />;
711
-
204
+
712
205
  return <ProductList products={data} />;
713
206
  } catch (error) {
714
207
  return <ErrorFallback error={error} />;
@@ -716,38 +209,16 @@ const SafeComponent = () => {
716
209
  };
717
210
  ```
718
211
 
719
- ### 2. Performance Optimization
720
-
721
- Use React.memo and useMemo for expensive operations:
722
-
723
- ```tsx
724
- const ProductCard = React.memo(({ product, onAddToCart }) => {
725
- const formattedPrice = React.useMemo(() =>
726
- new Intl.NumberFormat('en-US', {
727
- style: 'currency',
728
- currency: 'USD'
729
- }).format(product.price),
730
- [product.price]
731
- );
732
-
733
- return (
734
- <div className="product-card">
735
- <h3>{product.name}</h3>
736
- <p>{formattedPrice}</p>
737
- <button onClick={() => onAddToCart(product.id, 1)}>
738
- Add to Cart
739
- </button>
740
- </div>
741
- );
742
- });
743
- ```
744
-
745
- ### 3. TypeScript Best Practices
212
+ ### TypeScript Best Practices
746
213
 
747
214
  Leverage the provided types for better development experience:
748
215
 
749
216
  ```tsx
750
- import { Product, Category, useDataBridge } from '@dutchie/ecommerce-extensions-sdk';
217
+ import {
218
+ Product,
219
+ Category,
220
+ useDataBridge,
221
+ } from "@dutchie/ecommerce-extensions-sdk";
751
222
 
752
223
  interface ProductFilterProps {
753
224
  products: Product[];
@@ -755,23 +226,23 @@ interface ProductFilterProps {
755
226
  onFilterChange: (categoryId: string) => void;
756
227
  }
757
228
 
758
- const ProductFilter: React.FC<ProductFilterProps> = ({
759
- products,
760
- categories,
761
- onFilterChange
229
+ const ProductFilter: React.FC<ProductFilterProps> = ({
230
+ products,
231
+ categories,
232
+ onFilterChange,
762
233
  }) => {
763
234
  // Implementation
764
235
  };
765
236
  ```
766
237
 
767
- ### 4. Context Usage
238
+ ### Context Usage
768
239
 
769
240
  Always check for data availability before using:
770
241
 
771
242
  ```tsx
772
243
  const UserProfile = () => {
773
244
  const { user, actions } = useDataBridge();
774
-
245
+
775
246
  if (!user) {
776
247
  return (
777
248
  <div className="login-prompt">
@@ -780,7 +251,7 @@ const UserProfile = () => {
780
251
  </div>
781
252
  );
782
253
  }
783
-
254
+
784
255
  return (
785
256
  <div className="user-profile">
786
257
  <h2>Welcome, {user.firstName}!</h2>
@@ -790,49 +261,16 @@ const UserProfile = () => {
790
261
  };
791
262
  ```
792
263
 
793
- ## Development
794
-
795
- ### Setup
796
-
797
- ```bash
798
- # Install dependencies
799
- pnpm install
800
-
801
- # Start development mode
802
- pnpm dev
803
-
804
- # Build for production
805
- pnpm build
806
-
807
- # Run tests
808
- pnpm test
809
-
810
- # Format code
811
- pnpm format
812
-
813
- # Lint and fix
814
- pnpm check
815
- ```
816
-
817
- ### Building Extensions
818
-
819
- 1. Create your extension component
820
- 2. Set the `DataBridgeVersion` property
821
- 3. Export your component
822
- 4. Build and deploy using the Dutchie Pro Deployment platform
823
-
824
264
  ### Testing
825
265
 
826
- The SDK includes utilities for testing your extensions:
827
-
828
266
  ```tsx
829
- import { render, screen } from '@testing-library/react';
830
- import { DataBridgeContext } from '@dutchie/ecommerce-extensions-sdk';
831
- import MyExtension from './MyExtension';
267
+ import { render, screen } from "@testing-library/react";
268
+ import { DataBridgeContext } from "@dutchie/ecommerce-extensions-sdk";
269
+ import MyExtension from "./MyExtension";
832
270
 
833
271
  const mockDataBridge = {
834
- menuContext: 'store-front' as const,
835
- location: { id: '1', name: 'Test Dispensary' },
272
+ menuContext: "store-front" as const,
273
+ location: { id: "1", name: "Test Dispensary" },
836
274
  dataLoaders: {
837
275
  products: jest.fn().mockResolvedValue([]),
838
276
  // ... other loaders
@@ -840,20 +278,54 @@ const mockDataBridge = {
840
278
  actions: {
841
279
  addToCart: jest.fn(),
842
280
  // ... other actions
843
- }
281
+ },
844
282
  };
845
283
 
846
- test('renders extension correctly', () => {
284
+ test("renders extension correctly", () => {
847
285
  render(
848
286
  <DataBridgeContext.Provider value={mockDataBridge}>
849
287
  <MyExtension />
850
288
  </DataBridgeContext.Provider>
851
289
  );
852
-
853
- expect(screen.getByText('Test Dispensary')).toBeInTheDocument();
290
+
291
+ expect(screen.getByText("Test Dispensary")).toBeInTheDocument();
292
+ });
293
+ ```
294
+
295
+ ### Performance Optimization
296
+
297
+ Use `React.memo` and `useMemo` for expensive operations:
298
+
299
+ ```tsx
300
+ const ProductCard = React.memo(({ product, onAddToCart }) => {
301
+ const formattedPrice = React.useMemo(
302
+ () =>
303
+ new Intl.NumberFormat("en-US", {
304
+ style: "currency",
305
+ currency: "USD",
306
+ }).format(product.price),
307
+ [product.price]
308
+ );
309
+
310
+ return (
311
+ <div className="product-card">
312
+ <h3>{product.name}</h3>
313
+ <p>{formattedPrice}</p>
314
+ <button onClick={() => onAddToCart(product.id, 1)}>Add to Cart</button>
315
+ </div>
316
+ );
854
317
  });
855
318
  ```
856
319
 
320
+ ## Core Concepts
321
+
322
+ The Dutchie Ecommerce Extensions SDK is built around several key concepts:
323
+
324
+ 1. **Data Bridge**: A unified interface for accessing dispensary data, user information, and cart state
325
+ 2. **Actions**: Pre-built navigation and cart management functions
326
+ 3. **Remote Components**: Extension components that integrate seamlessly with the Dutchie platform
327
+ 4. **Context-Driven Architecture**: React context provides data and actions throughout your extension
328
+
857
329
  ## Support
858
330
 
859
331
  For technical support and questions about the Dutchie Ecommerce Extensions SDK:
@@ -861,8 +333,3 @@ For technical support and questions about the Dutchie Ecommerce Extensions SDK:
861
333
  - 📧 Contact your Dutchie agency partner representative
862
334
  - 📚 Refer to the Dutchie Pro platform documentation
863
335
  - 🐛 Report issues through the official Dutchie support channels
864
-
865
- ---
866
-
867
- **License:** MIT
868
- **Node Version:** >=16.0.0