create-brainerce-store 1.5.0 → 1.5.2

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.5.0",
34
+ version: "1.5.1",
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,6 +1,6 @@
1
1
  {
2
2
  "name": "create-brainerce-store",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "description": "Scaffold a production-ready e-commerce storefront connected to Brainerce",
5
5
  "bin": {
6
6
  "create-brainerce-store": "dist/index.js"
@@ -12,7 +12,7 @@ import { useTranslations } from '@/lib/translations';
12
12
 
13
13
  export default function AccountPage() {
14
14
  const router = useRouter();
15
- const { isLoggedIn, logout } = useAuth();
15
+ const { isLoggedIn, authLoading, logout } = useAuth();
16
16
  const t = useTranslations('account');
17
17
  const tc = useTranslations('common');
18
18
  const [profile, setProfile] = useState<CustomerProfile | null>(null);
@@ -21,6 +21,8 @@ export default function AccountPage() {
21
21
  const [error, setError] = useState<string | null>(null);
22
22
 
23
23
  useEffect(() => {
24
+ if (authLoading) return;
25
+
24
26
  if (!isLoggedIn) {
25
27
  router.push('/login');
26
28
  return;
@@ -52,9 +54,9 @@ export default function AccountPage() {
52
54
  }
53
55
 
54
56
  loadAccountData();
55
- }, [isLoggedIn, router]);
57
+ }, [isLoggedIn, authLoading, router]);
56
58
 
57
- if (!isLoggedIn) {
59
+ if (authLoading || !isLoggedIn) {
58
60
  return null;
59
61
  }
60
62
 
@@ -96,7 +98,9 @@ export default function AccountPage() {
96
98
  </div>
97
99
 
98
100
  {/* Profile Section */}
99
- {profile && <ProfileSection profile={profile} onProfileUpdate={setProfile} className="mb-8" />}
101
+ {profile && (
102
+ <ProfileSection profile={profile} onProfileUpdate={setProfile} className="mb-8" />
103
+ )}
100
104
 
101
105
  {/* Order History */}
102
106
  <div>
@@ -20,6 +20,7 @@ import { PaymentStep } from '@/components/checkout/payment-step';
20
20
  import { DeliveryMethodStep } from '@/components/checkout/delivery-method-step';
21
21
  import { PickupStep } from '@/components/checkout/pickup-step';
22
22
  import { TaxDisplay } from '@/components/checkout/tax-display';
23
+ import { CouponInput } from '@/components/cart/coupon-input';
23
24
  import { ReservationCountdown } from '@/components/cart/reservation-countdown';
24
25
  import { LoadingSpinner } from '@/components/shared/loading-spinner';
25
26
  import { useTranslations } from '@/lib/translations';
@@ -31,7 +32,7 @@ function CheckoutContent() {
31
32
  const searchParams = useSearchParams();
32
33
  const { storeInfo } = useStoreInfo();
33
34
  const { isLoggedIn } = useAuth();
34
- const { cart } = useCart();
35
+ const { cart, refreshCart } = useCart();
35
36
  const currency = storeInfo?.currency || 'USD';
36
37
  const t = useTranslations('checkout');
37
38
  const tc = useTranslations('common');
@@ -224,6 +225,20 @@ function CheckoutContent() {
224
225
  }
225
226
  }
226
227
 
228
+ // Refresh cart and checkout after coupon apply/remove
229
+ const handleCouponUpdate = useCallback(async () => {
230
+ await refreshCart();
231
+ if (checkout) {
232
+ try {
233
+ const client = getClient();
234
+ const updated = await client.getCheckout(checkout.id);
235
+ setCheckout(updated);
236
+ } catch (err) {
237
+ console.error('Failed to refresh checkout after coupon update:', err);
238
+ }
239
+ }
240
+ }, [checkout, refreshCart]);
241
+
227
242
  if (initializing) {
228
243
  return (
229
244
  <div className="flex min-h-[60vh] items-center justify-center">
@@ -541,6 +556,13 @@ function CheckoutContent() {
541
556
  )
542
557
  )}
543
558
 
559
+ {/* Coupon input */}
560
+ {cart && (
561
+ <div className="border-border border-t pt-4">
562
+ <CouponInput cart={cart} onUpdate={handleCouponUpdate} />
563
+ </div>
564
+ )}
565
+
544
566
  {/* Totals */}
545
567
  {checkout && (
546
568
  <div className="border-border space-y-2 border-t pt-4 text-sm">
@@ -86,7 +86,7 @@ export function ProfileSection({ profile, onProfileUpdate, className }: ProfileS
86
86
  type="text"
87
87
  value={form.firstName}
88
88
  onChange={(e) => setForm((f) => ({ ...f, firstName: e.target.value }))}
89
- className="border-border bg-background text-foreground w-full rounded-md border px-3 py-1.5 text-sm outline-none focus:border-primary"
89
+ className="border-border bg-background text-foreground focus:border-primary w-full rounded-md border px-3 py-1.5 text-sm outline-none"
90
90
  autoFocus
91
91
  />
92
92
  </div>
@@ -98,7 +98,7 @@ export function ProfileSection({ profile, onProfileUpdate, className }: ProfileS
98
98
  type="text"
99
99
  value={form.lastName}
100
100
  onChange={(e) => setForm((f) => ({ ...f, lastName: e.target.value }))}
101
- className="border-border bg-background text-foreground w-full rounded-md border px-3 py-1.5 text-sm outline-none focus:border-primary"
101
+ className="border-border bg-background text-foreground focus:border-primary w-full rounded-md border px-3 py-1.5 text-sm outline-none"
102
102
  />
103
103
  </div>
104
104
  </div>
@@ -110,7 +110,7 @@ export function ProfileSection({ profile, onProfileUpdate, className }: ProfileS
110
110
  type="tel"
111
111
  value={form.phone}
112
112
  onChange={(e) => setForm((f) => ({ ...f, phone: e.target.value }))}
113
- className="border-border bg-background text-foreground w-full rounded-md border px-3 py-1.5 text-sm outline-none focus:border-primary"
113
+ className="border-border bg-background text-foreground focus:border-primary w-full rounded-md border px-3 py-1.5 text-sm outline-none"
114
114
  />
115
115
  </div>
116
116
  <p className="text-muted-foreground truncate text-sm">{profile.email}</p>
@@ -144,8 +144,18 @@ export function ProfileSection({ profile, onProfileUpdate, className }: ProfileS
144
144
  className="text-muted-foreground hover:text-foreground flex-shrink-0 rounded p-1 transition-colors"
145
145
  title={t('editProfile')}
146
146
  >
147
- <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
148
- <path strokeLinecap="round" strokeLinejoin="round" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
147
+ <svg
148
+ className="h-4 w-4"
149
+ fill="none"
150
+ viewBox="0 0 24 24"
151
+ stroke="currentColor"
152
+ strokeWidth={2}
153
+ >
154
+ <path
155
+ strokeLinecap="round"
156
+ strokeLinejoin="round"
157
+ d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"
158
+ />
149
159
  </svg>
150
160
  </button>
151
161
  </div>
@@ -179,7 +189,9 @@ export function ProfileSection({ profile, onProfileUpdate, className }: ProfileS
179
189
  )}
180
190
  </div>
181
191
 
182
- {profile.phone && <p className="text-muted-foreground mt-2 text-sm">{profile.phone}</p>}
192
+ {profile.phone && (
193
+ <p className="text-muted-foreground mt-2 text-sm">{profile.phone}</p>
194
+ )}
183
195
 
184
196
  <p className="text-muted-foreground mt-3 text-xs">
185
197
  {t('memberSince')}{' '}
@@ -197,7 +209,9 @@ export function ProfileSection({ profile, onProfileUpdate, className }: ProfileS
197
209
  <p
198
210
  className={cn(
199
211
  'mt-2 text-sm',
200
- message.type === 'success' ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'
212
+ message.type === 'success'
213
+ ? 'text-green-600 dark:text-green-400'
214
+ : 'text-red-600 dark:text-red-400'
201
215
  )}
202
216
  >
203
217
  {message.text}
@@ -23,6 +23,7 @@ export function useStoreInfo() {
23
23
  // ---- Auth Context ----
24
24
  interface AuthContextValue {
25
25
  isLoggedIn: boolean;
26
+ authLoading: boolean;
26
27
  token: string | null;
27
28
  login: (token: string) => void;
28
29
  logout: () => void;
@@ -30,6 +31,7 @@ interface AuthContextValue {
30
31
 
31
32
  const AuthContext = createContext<AuthContextValue>({
32
33
  isLoggedIn: false,
34
+ authLoading: true,
33
35
  token: null,
34
36
  login: () => {},
35
37
  logout: () => {},
@@ -65,6 +67,7 @@ export function StoreProvider({ children }: { children: React.ReactNode }) {
65
67
  const [storeInfo, setStoreInfo] = useState<StoreInfo | null>(null);
66
68
  const [storeLoading, setStoreLoading] = useState(true);
67
69
  const [token, setToken] = useState<string | null>(null);
70
+ const [authLoading, setAuthLoading] = useState(true);
68
71
  const [cart, setCart] = useState<Cart | null>(null);
69
72
  const [cartLoading, setCartLoading] = useState(true);
70
73
 
@@ -75,6 +78,7 @@ export function StoreProvider({ children }: { children: React.ReactNode }) {
75
78
  if (stored) {
76
79
  setToken(stored);
77
80
  }
81
+ setAuthLoading(false);
78
82
 
79
83
  client
80
84
  .getStoreInfo()
@@ -134,7 +138,7 @@ export function StoreProvider({ children }: { children: React.ReactNode }) {
134
138
 
135
139
  return (
136
140
  <StoreInfoContext.Provider value={{ storeInfo, loading: storeLoading }}>
137
- <AuthContext.Provider value={{ isLoggedIn: !!token, token, login, logout }}>
141
+ <AuthContext.Provider value={{ isLoggedIn: !!token, authLoading, token, login, logout }}>
138
142
  <CartContext.Provider
139
143
  value={{ cart, cartLoading, refreshCart, itemCount, totals }}
140
144
  >