create-brainerce-store 1.0.1 → 1.2.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/dist/index.js CHANGED
@@ -31,7 +31,7 @@ var require_package = __commonJS({
31
31
  "package.json"(exports2, module2) {
32
32
  module2.exports = {
33
33
  name: "create-brainerce-store",
34
- version: "1.0.1",
34
+ version: "1.2.0",
35
35
  description: "Scaffold a production-ready e-commerce storefront connected to Brainerce",
36
36
  bin: {
37
37
  "create-brainerce-store": "dist/index.js"
package/package.json CHANGED
@@ -1,44 +1,44 @@
1
- {
2
- "name": "create-brainerce-store",
3
- "version": "1.0.1",
4
- "description": "Scaffold a production-ready e-commerce storefront connected to Brainerce",
5
- "bin": {
6
- "create-brainerce-store": "dist/index.js"
7
- },
8
- "files": [
9
- "dist",
10
- "templates"
11
- ],
12
- "scripts": {
13
- "build": "tsup src/index.ts --format cjs --dts --clean",
14
- "dev": "tsup src/index.ts --format cjs --dts --watch",
15
- "clean": "rimraf dist"
16
- },
17
- "dependencies": {
18
- "chalk": "^4.1.2",
19
- "commander": "^12.1.0",
20
- "ejs": "^3.1.10",
21
- "fs-extra": "^11.2.0",
22
- "ora": "^5.4.1",
23
- "prompts": "^2.4.2"
24
- },
25
- "devDependencies": {
26
- "@types/ejs": "^3.1.5",
27
- "@types/fs-extra": "^11.0.4",
28
- "@types/prompts": "^2.4.9",
29
- "tsup": "^8.0.0",
30
- "typescript": "^5.4.0"
31
- },
32
- "engines": {
33
- "node": ">=18"
34
- },
35
- "keywords": [
36
- "brainerce",
37
- "ecommerce",
38
- "storefront",
39
- "scaffold",
40
- "create",
41
- "nextjs"
42
- ],
43
- "license": "MIT"
44
- }
1
+ {
2
+ "name": "create-brainerce-store",
3
+ "version": "1.2.0",
4
+ "description": "Scaffold a production-ready e-commerce storefront connected to Brainerce",
5
+ "bin": {
6
+ "create-brainerce-store": "dist/index.js"
7
+ },
8
+ "files": [
9
+ "dist",
10
+ "templates"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsup src/index.ts --format cjs --dts --clean",
14
+ "dev": "tsup src/index.ts --format cjs --dts --watch",
15
+ "clean": "rimraf dist"
16
+ },
17
+ "dependencies": {
18
+ "chalk": "^4.1.2",
19
+ "commander": "^12.1.0",
20
+ "ejs": "^3.1.10",
21
+ "fs-extra": "^11.2.0",
22
+ "ora": "^5.4.1",
23
+ "prompts": "^2.4.2"
24
+ },
25
+ "devDependencies": {
26
+ "@types/ejs": "^3.1.5",
27
+ "@types/fs-extra": "^11.0.4",
28
+ "@types/prompts": "^2.4.9",
29
+ "tsup": "^8.0.0",
30
+ "typescript": "^5.4.0"
31
+ },
32
+ "engines": {
33
+ "node": ">=18"
34
+ },
35
+ "keywords": [
36
+ "brainerce",
37
+ "ecommerce",
38
+ "storefront",
39
+ "scaffold",
40
+ "create",
41
+ "nextjs"
42
+ ],
43
+ "license": "MIT"
44
+ }
@@ -1,11 +1,7 @@
1
1
  'use client';
2
2
 
3
- import { useState } from 'react';
4
3
  import Link from 'next/link';
5
- import Image from 'next/image';
6
- import type { LocalCart, LocalCartItem } from 'brainerce';
7
- import { formatPrice } from 'brainerce';
8
- import { useStoreInfo, useCart } from '@/providers/store-provider';
4
+ import { useCart } from '@/providers/store-provider';
9
5
  import { CartItem } from '@/components/cart/cart-item';
10
6
  import { CartSummary } from '@/components/cart/cart-summary';
11
7
  import { CouponInput } from '@/components/cart/coupon-input';
@@ -14,9 +10,7 @@ import { ReservationCountdown } from '@/components/cart/reservation-countdown';
14
10
  import { LoadingSpinner } from '@/components/shared/loading-spinner';
15
11
 
16
12
  export default function CartPage() {
17
- const { storeInfo } = useStoreInfo();
18
- const { cart, cartLoading, refreshCart, itemCount, isServerCart } = useCart();
19
- const currency = storeInfo?.currency || 'USD';
13
+ const { cart, cartLoading, refreshCart, itemCount } = useCart();
20
14
 
21
15
  if (cartLoading) {
22
16
  return (
@@ -57,8 +51,6 @@ export default function CartPage() {
57
51
  );
58
52
  }
59
53
 
60
- const serverCart = isServerCart(cart) ? cart : null;
61
-
62
54
  return (
63
55
  <div className="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">
64
56
  <h1 className="text-foreground mb-6 text-2xl font-bold">
@@ -66,47 +58,29 @@ export default function CartPage() {
66
58
  </h1>
67
59
 
68
60
  {/* Reservation countdown */}
69
- {serverCart?.reservation?.hasReservation && (
70
- <ReservationCountdown reservation={serverCart.reservation} className="mb-6" />
61
+ {cart.reservation?.hasReservation && (
62
+ <ReservationCountdown reservation={cart.reservation} className="mb-6" />
71
63
  )}
72
64
 
73
65
  <div className="grid grid-cols-1 gap-8 lg:grid-cols-3">
74
66
  {/* Cart Items */}
75
67
  <div className="lg:col-span-2">
76
68
  {/* Nudges */}
77
- {serverCart?.nudges && serverCart.nudges.length > 0 && (
78
- <CartNudges nudges={serverCart.nudges} className="mb-4" />
79
- )}
80
-
81
- {/* Server cart items */}
82
- {serverCart && (
83
- <div>
84
- {serverCart.items.map((item) => (
85
- <CartItem key={item.id} item={item} onUpdate={refreshCart} />
86
- ))}
87
- </div>
69
+ {cart.nudges && cart.nudges.length > 0 && (
70
+ <CartNudges nudges={cart.nudges} className="mb-4" />
88
71
  )}
89
72
 
90
- {/* Local cart items (guest) */}
91
- {!serverCart && (
92
- <div>
93
- {(cart as LocalCart).items.map((item: LocalCartItem) => (
94
- <LocalCartItemRow
95
- key={`${item.productId}-${item.variantId || 'default'}`}
96
- item={item}
97
- currency={currency}
98
- onUpdate={refreshCart}
99
- />
100
- ))}
101
- </div>
102
- )}
73
+ {/* Cart items */}
74
+ <div>
75
+ {cart.items.map((item) => (
76
+ <CartItem key={item.id} item={item} onUpdate={refreshCart} />
77
+ ))}
78
+ </div>
103
79
 
104
- {/* Coupon input - server cart only */}
105
- {serverCart && (
106
- <div className="border-border mt-6 border-t pt-4">
107
- <CouponInput cart={serverCart} onUpdate={refreshCart} />
108
- </div>
109
- )}
80
+ {/* Coupon input */}
81
+ <div className="border-border mt-6 border-t pt-4">
82
+ <CouponInput cart={cart} onUpdate={refreshCart} />
83
+ </div>
110
84
  </div>
111
85
 
112
86
  {/* Summary sidebar */}
@@ -133,131 +107,3 @@ export default function CartPage() {
133
107
  </div>
134
108
  );
135
109
  }
136
-
137
- // Local cart item display for guest users
138
- function LocalCartItemRow({
139
- item,
140
- currency,
141
- onUpdate,
142
- }: {
143
- item: LocalCartItem;
144
- currency: string;
145
- onUpdate: () => void;
146
- }) {
147
- const [updating, setUpdating] = useState(false);
148
- const [removing, setRemoving] = useState(false);
149
-
150
- const unitPrice = parseFloat(item.price || '0');
151
- const lineTotal = unitPrice * item.quantity;
152
-
153
- async function handleQuantityChange(newQuantity: number) {
154
- if (newQuantity < 1 || updating) return;
155
-
156
- try {
157
- setUpdating(true);
158
- const { getClient } = await import('@/lib/brainerce');
159
- const client = getClient();
160
- await client.smartUpdateCartItem(item.productId, newQuantity, item.variantId);
161
- onUpdate();
162
- } catch (err) {
163
- console.error('Failed to update quantity:', err);
164
- } finally {
165
- setUpdating(false);
166
- }
167
- }
168
-
169
- async function handleRemove() {
170
- if (removing) return;
171
-
172
- try {
173
- setRemoving(true);
174
- const { getClient } = await import('@/lib/brainerce');
175
- const client = getClient();
176
- await client.smartRemoveFromCart(item.productId, item.variantId);
177
- onUpdate();
178
- } catch (err) {
179
- console.error('Failed to remove item:', err);
180
- } finally {
181
- setRemoving(false);
182
- }
183
- }
184
-
185
- return (
186
- <div className="border-border flex gap-4 border-b py-4 last:border-0">
187
- {/* Image */}
188
- <div className="bg-muted relative h-20 w-20 flex-shrink-0 overflow-hidden rounded">
189
- {item.image ? (
190
- <Image
191
- src={item.image}
192
- alt={item.name || 'Product'}
193
- fill
194
- sizes="80px"
195
- className="object-cover"
196
- />
197
- ) : (
198
- <div className="text-muted-foreground absolute inset-0 flex items-center justify-center">
199
- <svg className="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
200
- <path
201
- strokeLinecap="round"
202
- strokeLinejoin="round"
203
- strokeWidth={1.5}
204
- d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
205
- />
206
- </svg>
207
- </div>
208
- )}
209
- </div>
210
-
211
- {/* Details */}
212
- <div className="min-w-0 flex-1">
213
- <h3 className="text-foreground truncate text-sm font-medium">{item.name || 'Product'}</h3>
214
-
215
- <p className="text-muted-foreground mt-1 text-sm">
216
- {formatPrice(unitPrice, { currency }) as string}
217
- </p>
218
-
219
- <div className="mt-2 flex items-center gap-3">
220
- <div className="border-border flex items-center rounded border">
221
- <button
222
- type="button"
223
- onClick={() => handleQuantityChange(item.quantity - 1)}
224
- disabled={updating || item.quantity <= 1}
225
- className="text-foreground hover:bg-muted px-2 py-1 text-sm transition-colors disabled:cursor-not-allowed disabled:opacity-40"
226
- aria-label="Decrease quantity"
227
- >
228
- -
229
- </button>
230
- <span className="text-foreground min-w-[2.5rem] px-3 py-1 text-center text-sm font-medium">
231
- {item.quantity}
232
- </span>
233
- <button
234
- type="button"
235
- onClick={() => handleQuantityChange(item.quantity + 1)}
236
- disabled={updating}
237
- className="text-foreground hover:bg-muted px-2 py-1 text-sm transition-colors disabled:cursor-not-allowed disabled:opacity-40"
238
- aria-label="Increase quantity"
239
- >
240
- +
241
- </button>
242
- </div>
243
-
244
- <button
245
- type="button"
246
- onClick={handleRemove}
247
- disabled={removing}
248
- className="text-destructive hover:text-destructive/80 text-xs transition-colors disabled:opacity-40"
249
- >
250
- {removing ? 'Removing...' : 'Remove'}
251
- </button>
252
- </div>
253
- </div>
254
-
255
- {/* Line total */}
256
- <div className="flex-shrink-0 text-end">
257
- <span className="text-foreground text-sm font-medium">
258
- {formatPrice(lineTotal, { currency }) as string}
259
- </span>
260
- </div>
261
- </div>
262
- );
263
- }