@dutchiesdk/ecommerce-extensions-sdk 0.6.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 +868 -0
- package/dist/context/ecommerce-data-bridge.d.ts +8 -0
- package/dist/context/ecommerce-data-bridge.js +22 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/types/dutchie-data-bridge.d.ts +109 -0
- package/dist/types/dutchie-data-bridge.js +0 -0
- package/dist/types/ecommerce-extension.d.ts +16 -0
- package/dist/types/ecommerce-extension.js +0 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,868 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
# @dutchiesdk/ecommerce-extensions-sdk
|
|
6
|
+
|
|
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.
|
|
8
|
+
|
|
9
|
+
> ⚠️ **Alpha Release Warning**
|
|
10
|
+
>
|
|
11
|
+
> 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
|
+
|
|
13
|
+
## Development Environment Access
|
|
14
|
+
|
|
15
|
+
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
|
+
|
|
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)
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install @dutchie/ecommerce-extensions-sdk
|
|
40
|
+
# or
|
|
41
|
+
yarn add @dutchie/ecommerce-extensions-sdk
|
|
42
|
+
# or
|
|
43
|
+
pnpm add @dutchie/ecommerce-extensions-sdk
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import React from 'react';
|
|
50
|
+
import { useDataBridge, RemoteBoundaryComponent } from '@dutchie/ecommerce-extensions-sdk';
|
|
51
|
+
|
|
52
|
+
const MyExtension: RemoteBoundaryComponent = () => {
|
|
53
|
+
const { dataLoaders, actions, location, user, cart } = useDataBridge();
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div>
|
|
57
|
+
<h1>Welcome to {location?.name}</h1>
|
|
58
|
+
<p>Cart items: {cart?.items.length || 0}</p>
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
MyExtension.DataBridgeVersion = '0.2';
|
|
64
|
+
|
|
65
|
+
export default MyExtension;
|
|
66
|
+
```
|
|
67
|
+
|
|
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
|
+
## React Context & Hooks
|
|
78
|
+
|
|
79
|
+
### `useDataBridge()`
|
|
80
|
+
|
|
81
|
+
The primary hook for accessing the Dutchie platform data and functionality.
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
import { useDataBridge } from '@dutchie/ecommerce-extensions-sdk';
|
|
85
|
+
|
|
86
|
+
const MyComponent = () => {
|
|
87
|
+
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
|
|
94
|
+
} = useDataBridge();
|
|
95
|
+
|
|
96
|
+
// Component logic here
|
|
97
|
+
};
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Throws**: Error if used outside of a `DataBridgeProvider`
|
|
101
|
+
|
|
102
|
+
### `useAsyncLoader(fn)`
|
|
103
|
+
|
|
104
|
+
A utility hook for handling async data loading with loading states.
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
import { useAsyncLoader, useDataBridge } from '@dutchie/ecommerce-extensions-sdk';
|
|
108
|
+
|
|
109
|
+
const ProductList = () => {
|
|
110
|
+
const { dataLoaders } = useDataBridge();
|
|
111
|
+
const { data: products, isLoading } = useAsyncLoader(dataLoaders.products);
|
|
112
|
+
|
|
113
|
+
if (isLoading) return <div>Loading products...</div>;
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<div>
|
|
117
|
+
{products?.map(product => (
|
|
118
|
+
<div key={product.id}>{product.name}</div>
|
|
119
|
+
))}
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
};
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Parameters:**
|
|
126
|
+
- `fn`: `() => Promise<unknown>` - Async function to execute
|
|
127
|
+
|
|
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`
|
|
137
|
+
|
|
138
|
+
The main interface providing access to all platform data and functionality.
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
interface CommerceComponentsDataInterface {
|
|
142
|
+
menuContext: 'store-front' | 'kiosk';
|
|
143
|
+
location?: Dispensary;
|
|
144
|
+
user?: User;
|
|
145
|
+
cart?: Cart;
|
|
146
|
+
dataLoaders: DataLoaders;
|
|
147
|
+
actions: Actions;
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
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
|
+
## Extension Components
|
|
305
|
+
|
|
306
|
+
### `RemoteBoundaryComponent`
|
|
307
|
+
|
|
308
|
+
The base type for all extension components that integrate with the Dutchie platform.
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
type RemoteBoundaryComponent = React.FC & {
|
|
312
|
+
DataBridgeVersion: string;
|
|
313
|
+
};
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Example:**
|
|
317
|
+
|
|
318
|
+
```tsx
|
|
319
|
+
const MyCustomHeader: RemoteBoundaryComponent = () => {
|
|
320
|
+
const { location, user, actions } = useDataBridge();
|
|
321
|
+
|
|
322
|
+
return (
|
|
323
|
+
<header>
|
|
324
|
+
<h1>{location?.name}</h1>
|
|
325
|
+
{user ? (
|
|
326
|
+
<span>Welcome, {user.firstName}!</span>
|
|
327
|
+
) : (
|
|
328
|
+
<button onClick={actions.goToLogin}>Login</button>
|
|
329
|
+
)}
|
|
330
|
+
</header>
|
|
331
|
+
);
|
|
332
|
+
};
|
|
333
|
+
|
|
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
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## Actions API
|
|
359
|
+
|
|
360
|
+
The actions object provides pre-built functions for common e-commerce operations.
|
|
361
|
+
|
|
362
|
+
### Navigation Actions
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
const { actions } = useDataBridge();
|
|
366
|
+
|
|
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
|
|
376
|
+
|
|
377
|
+
// 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
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Cart Actions
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
const { actions } = useDataBridge();
|
|
393
|
+
|
|
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
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
## Data Loaders
|
|
406
|
+
|
|
407
|
+
Async functions for loading platform data. All loaders return promises that resolve to arrays.
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
const { dataLoaders } = useDataBridge();
|
|
411
|
+
|
|
412
|
+
// 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
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
**Note:** All data loaders return empty arrays (`[]`) when no data is available.
|
|
424
|
+
|
|
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
|
+
## Best Practices
|
|
698
|
+
|
|
699
|
+
### 1. Error Handling
|
|
700
|
+
|
|
701
|
+
Always wrap `useDataBridge()` calls in error boundaries and handle loading states:
|
|
702
|
+
|
|
703
|
+
```tsx
|
|
704
|
+
const SafeComponent = () => {
|
|
705
|
+
try {
|
|
706
|
+
const { dataLoaders } = useDataBridge();
|
|
707
|
+
const { data, isLoading } = useAsyncLoader(dataLoaders.products);
|
|
708
|
+
|
|
709
|
+
if (isLoading) return <LoadingSpinner />;
|
|
710
|
+
if (!data) return <ErrorMessage message="Failed to load products" />;
|
|
711
|
+
|
|
712
|
+
return <ProductList products={data} />;
|
|
713
|
+
} catch (error) {
|
|
714
|
+
return <ErrorFallback error={error} />;
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
```
|
|
718
|
+
|
|
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
|
|
746
|
+
|
|
747
|
+
Leverage the provided types for better development experience:
|
|
748
|
+
|
|
749
|
+
```tsx
|
|
750
|
+
import { Product, Category, useDataBridge } from '@dutchie/ecommerce-extensions-sdk';
|
|
751
|
+
|
|
752
|
+
interface ProductFilterProps {
|
|
753
|
+
products: Product[];
|
|
754
|
+
categories: Category[];
|
|
755
|
+
onFilterChange: (categoryId: string) => void;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const ProductFilter: React.FC<ProductFilterProps> = ({
|
|
759
|
+
products,
|
|
760
|
+
categories,
|
|
761
|
+
onFilterChange
|
|
762
|
+
}) => {
|
|
763
|
+
// Implementation
|
|
764
|
+
};
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
### 4. Context Usage
|
|
768
|
+
|
|
769
|
+
Always check for data availability before using:
|
|
770
|
+
|
|
771
|
+
```tsx
|
|
772
|
+
const UserProfile = () => {
|
|
773
|
+
const { user, actions } = useDataBridge();
|
|
774
|
+
|
|
775
|
+
if (!user) {
|
|
776
|
+
return (
|
|
777
|
+
<div className="login-prompt">
|
|
778
|
+
<p>Please log in to view your profile</p>
|
|
779
|
+
<button onClick={actions.goToLogin}>Login</button>
|
|
780
|
+
</div>
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
return (
|
|
785
|
+
<div className="user-profile">
|
|
786
|
+
<h2>Welcome, {user.firstName}!</h2>
|
|
787
|
+
{/* Profile content */}
|
|
788
|
+
</div>
|
|
789
|
+
);
|
|
790
|
+
};
|
|
791
|
+
```
|
|
792
|
+
|
|
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
|
+
### Testing
|
|
825
|
+
|
|
826
|
+
The SDK includes utilities for testing your extensions:
|
|
827
|
+
|
|
828
|
+
```tsx
|
|
829
|
+
import { render, screen } from '@testing-library/react';
|
|
830
|
+
import { DataBridgeContext } from '@dutchie/ecommerce-extensions-sdk';
|
|
831
|
+
import MyExtension from './MyExtension';
|
|
832
|
+
|
|
833
|
+
const mockDataBridge = {
|
|
834
|
+
menuContext: 'store-front' as const,
|
|
835
|
+
location: { id: '1', name: 'Test Dispensary' },
|
|
836
|
+
dataLoaders: {
|
|
837
|
+
products: jest.fn().mockResolvedValue([]),
|
|
838
|
+
// ... other loaders
|
|
839
|
+
},
|
|
840
|
+
actions: {
|
|
841
|
+
addToCart: jest.fn(),
|
|
842
|
+
// ... other actions
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
|
|
846
|
+
test('renders extension correctly', () => {
|
|
847
|
+
render(
|
|
848
|
+
<DataBridgeContext.Provider value={mockDataBridge}>
|
|
849
|
+
<MyExtension />
|
|
850
|
+
</DataBridgeContext.Provider>
|
|
851
|
+
);
|
|
852
|
+
|
|
853
|
+
expect(screen.getByText('Test Dispensary')).toBeInTheDocument();
|
|
854
|
+
});
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
## Support
|
|
858
|
+
|
|
859
|
+
For technical support and questions about the Dutchie Ecommerce Extensions SDK:
|
|
860
|
+
|
|
861
|
+
- 📧 Contact your Dutchie agency partner representative
|
|
862
|
+
- 📚 Refer to the Dutchie Pro platform documentation
|
|
863
|
+
- 🐛 Report issues through the official Dutchie support channels
|
|
864
|
+
|
|
865
|
+
---
|
|
866
|
+
|
|
867
|
+
**License:** MIT
|
|
868
|
+
**Node Version:** >=16.0.0
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { CommerceComponentsDataInterface } from "../types/dutchie-data-bridge";
|
|
2
|
+
export declare const DutchieDataBridgeVersion: string;
|
|
3
|
+
export declare const DutchieDataBridgeContext: import("react").Context<CommerceComponentsDataInterface | undefined>;
|
|
4
|
+
export declare const useDutchieDataBridge: () => CommerceComponentsDataInterface;
|
|
5
|
+
export declare const useAsyncLoader: (fn: () => Promise<unknown>) => {
|
|
6
|
+
data: unknown;
|
|
7
|
+
isLoading: boolean;
|
|
8
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as __WEBPACK_EXTERNAL_MODULE_react__ from "react";
|
|
2
|
+
import * as __WEBPACK_EXTERNAL_MODULE__package_json_1116eba2__ from "../../package.json";
|
|
3
|
+
const DutchieDataBridgeVersion = __WEBPACK_EXTERNAL_MODULE__package_json_1116eba2__["default"].version;
|
|
4
|
+
const DutchieDataBridgeContext = (0, __WEBPACK_EXTERNAL_MODULE_react__.createContext)(void 0);
|
|
5
|
+
const useDutchieDataBridge = ()=>{
|
|
6
|
+
const context = (0, __WEBPACK_EXTERNAL_MODULE_react__.useContext)(DutchieDataBridgeContext);
|
|
7
|
+
if (void 0 === context) throw new Error('useDutchieDataBridge must be used within a DutchieDataBridgeProvider');
|
|
8
|
+
return context;
|
|
9
|
+
};
|
|
10
|
+
const useAsyncLoader = (fn)=>{
|
|
11
|
+
const [data, setData] = (0, __WEBPACK_EXTERNAL_MODULE_react__.useState)(null);
|
|
12
|
+
(0, __WEBPACK_EXTERNAL_MODULE_react__.useEffect)(()=>{
|
|
13
|
+
fn().then(setData);
|
|
14
|
+
}, [
|
|
15
|
+
fn
|
|
16
|
+
]);
|
|
17
|
+
return {
|
|
18
|
+
data,
|
|
19
|
+
isLoading: null === data
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
export { DutchieDataBridgeContext, DutchieDataBridgeVersion, useAsyncLoader, useDutchieDataBridge };
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export type CommerceComponentsDataInterface = {
|
|
2
|
+
menuContext: CommerceComponentsMenuContext;
|
|
3
|
+
location?: Dispensary;
|
|
4
|
+
user?: User;
|
|
5
|
+
cart?: Cart;
|
|
6
|
+
dataLoaders: {
|
|
7
|
+
getAllLocations: () => Promise<Dispensary[] | []>;
|
|
8
|
+
};
|
|
9
|
+
actions: {
|
|
10
|
+
goToInfoPage: () => void;
|
|
11
|
+
goToStoreFront: () => void;
|
|
12
|
+
goToStoreLocator: () => void;
|
|
13
|
+
goToStoreBrowser: () => void;
|
|
14
|
+
openLogin: () => void;
|
|
15
|
+
openSignUp: () => void;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export type HoursSettingsForOrderType = {
|
|
19
|
+
enabled: boolean;
|
|
20
|
+
effectiveHours?: {
|
|
21
|
+
Monday?: DayHours;
|
|
22
|
+
Tuesday?: DayHours;
|
|
23
|
+
Wednesday?: DayHours;
|
|
24
|
+
Thursday?: DayHours;
|
|
25
|
+
Friday?: DayHours;
|
|
26
|
+
Saturday?: DayHours;
|
|
27
|
+
Sunday?: DayHours;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
export type DayHours = {
|
|
31
|
+
active?: boolean;
|
|
32
|
+
start?: string;
|
|
33
|
+
end?: string;
|
|
34
|
+
};
|
|
35
|
+
export type DispensaryHoursSettings = {
|
|
36
|
+
inStorePickup?: HoursSettingsForOrderType;
|
|
37
|
+
curbsidePickup?: HoursSettingsForOrderType;
|
|
38
|
+
driveThruPickup?: HoursSettingsForOrderType;
|
|
39
|
+
delivery?: HoursSettingsForOrderType;
|
|
40
|
+
};
|
|
41
|
+
export type OrderTypeConfig = {
|
|
42
|
+
enableASAPOrdering?: boolean;
|
|
43
|
+
enableScheduledOrdering?: boolean;
|
|
44
|
+
enableAfterHoursOrdering?: boolean;
|
|
45
|
+
};
|
|
46
|
+
export type OrderTypesConfigV2 = {
|
|
47
|
+
inStorePickup?: OrderTypeConfig;
|
|
48
|
+
curbsidePickup?: OrderTypeConfig;
|
|
49
|
+
driveThruPickup?: OrderTypeConfig;
|
|
50
|
+
delivery?: OrderTypeConfig;
|
|
51
|
+
offerAnyPickupService?: boolean;
|
|
52
|
+
offerDeliveryService?: boolean;
|
|
53
|
+
};
|
|
54
|
+
export type DispensaryOrderTypes = {
|
|
55
|
+
pickup: boolean;
|
|
56
|
+
inStorePickup: boolean;
|
|
57
|
+
curbsidePickup: boolean;
|
|
58
|
+
driveThruPickup: boolean;
|
|
59
|
+
delivery: boolean;
|
|
60
|
+
kiosk: boolean;
|
|
61
|
+
};
|
|
62
|
+
export type DispensaryAddress = {
|
|
63
|
+
street1: string;
|
|
64
|
+
street2: string;
|
|
65
|
+
city: string;
|
|
66
|
+
state: string;
|
|
67
|
+
stateAbbreviation: string;
|
|
68
|
+
zip: string;
|
|
69
|
+
};
|
|
70
|
+
export type Dispensary = {
|
|
71
|
+
id: string;
|
|
72
|
+
status: string;
|
|
73
|
+
name: string;
|
|
74
|
+
cname: string;
|
|
75
|
+
chain: string;
|
|
76
|
+
phone: string;
|
|
77
|
+
email: string;
|
|
78
|
+
hours: DispensaryHoursSettings;
|
|
79
|
+
orderTypes: DispensaryOrderTypes;
|
|
80
|
+
images: {
|
|
81
|
+
logo: string;
|
|
82
|
+
};
|
|
83
|
+
address: DispensaryAddress;
|
|
84
|
+
links: {
|
|
85
|
+
website: string;
|
|
86
|
+
storeFrontRoot: string;
|
|
87
|
+
};
|
|
88
|
+
orderTypesConfig: OrderTypesConfigV2;
|
|
89
|
+
};
|
|
90
|
+
export type User = {
|
|
91
|
+
email: string;
|
|
92
|
+
firstName: string;
|
|
93
|
+
lastName: string;
|
|
94
|
+
birthday: string;
|
|
95
|
+
};
|
|
96
|
+
export type CartItem = {
|
|
97
|
+
productId: string;
|
|
98
|
+
name: string;
|
|
99
|
+
price: number;
|
|
100
|
+
quantity: number;
|
|
101
|
+
};
|
|
102
|
+
export type Cart = {
|
|
103
|
+
items: CartItem[];
|
|
104
|
+
total: number;
|
|
105
|
+
subtotal: number;
|
|
106
|
+
tax: number;
|
|
107
|
+
discount: number;
|
|
108
|
+
};
|
|
109
|
+
export type CommerceComponentsMenuContext = 'store-front' | 'kiosk';
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type RemoteBoundaryComponent = React.FC & {
|
|
2
|
+
DataBridgeVersion: string;
|
|
3
|
+
};
|
|
4
|
+
export type RoutablePageRegistryEntry = {
|
|
5
|
+
path: string;
|
|
6
|
+
component: RemoteBoundaryComponent;
|
|
7
|
+
};
|
|
8
|
+
export type RemoteModuleRegistry = {
|
|
9
|
+
RouteablePages?: RoutablePageRegistryEntry[];
|
|
10
|
+
StoreFrontHeader?: RemoteBoundaryComponent;
|
|
11
|
+
StoreFrontNavigation?: RemoteBoundaryComponent;
|
|
12
|
+
StoreFrontFooter?: RemoteBoundaryComponent;
|
|
13
|
+
StoreFrontHero?: RemoteBoundaryComponent;
|
|
14
|
+
ProductDetailsPrimary?: RemoteBoundaryComponent;
|
|
15
|
+
ProductDetailsSecondary?: RemoteBoundaryComponent;
|
|
16
|
+
};
|
|
File without changes
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dutchiesdk/ecommerce-extensions-sdk",
|
|
3
|
+
"private": false,
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"version": "0.6.0",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"module": "./dist/esm/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/esm/index.js",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=16.0.0"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "rslib build",
|
|
27
|
+
"check": "biome check --write",
|
|
28
|
+
"dev": "rslib build --watch",
|
|
29
|
+
"format": "biome format --write",
|
|
30
|
+
"test": "vitest run"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@biomejs/biome": "^1.9.4",
|
|
34
|
+
"@rsbuild/plugin-react": "^1.3.2",
|
|
35
|
+
"@rslib/core": "^0.9.2",
|
|
36
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
37
|
+
"@testing-library/react": "^16.3.0",
|
|
38
|
+
"@types/react": "^17.0.0",
|
|
39
|
+
"jsdom": "^26.1.0",
|
|
40
|
+
"react": "^17.0.0",
|
|
41
|
+
"typescript": "^5.8.3",
|
|
42
|
+
"vitest": "^3.2.2"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"react": "^17.0.0 || ^18.0.0",
|
|
46
|
+
"react-dom": "^17.0.0 || ^18.0.0"
|
|
47
|
+
},
|
|
48
|
+
"packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610"
|
|
49
|
+
}
|