create-brainerce-store 1.2.0 → 1.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/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.2.0",
34
+ version: "1.2.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.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Scaffold a production-ready e-commerce storefront connected to Brainerce",
5
5
  "bin": {
6
6
  "create-brainerce-store": "dist/index.js"
@@ -0,0 +1,26 @@
1
+ // Apple Pay domain verification for Grow (Meshulam) payment provider
2
+ // This file must be accessible at /.well-known/apple-developer-merchantid-domain-association
3
+ // See: https://developer.apple.com/documentation/apple_pay_on_the_web/setting_up_your_server
4
+
5
+ import { NextResponse } from 'next/server';
6
+
7
+ // Production domain verification content from Grow/Meshulam
8
+ const PRODUCTION_VERIFICATION =
9
+ '7B227073704964223A2231344636453432313941364637443346353446454633434638423534424630323337344445383438373643463936304544434643313836383831344136353933222C2276657273696F6E223A312C22637265617465644F6E223A313632323635363735313134342C227369676E6174757265223A2233303830303630393261383634383836663730643031303730326130383033303830303230313031333130663330306430363039363038363438303136353033303430323031303530303330383030363039326138363438383666373064303130373031303030306130383033303832303365333330383230333838613030333032303130323032303834633330343134393531396435343336333030613036303832613836343863653364303430333032333037613331326533303263303630333535303430333063323534313730373036633635323034313730373036633639363336313734363936663665323034393665373436353637373236313734363936663665323034333431323032643230343733333331323633303234303630333535303430623063316434313730373036633635323034333635373237343639363636393633363137343639366636653230343137353734363836663732363937343739333131333330313130363033353530343061306330613431373037303663363532303439366536333265333130623330303930363033353530343036313330323535353333303165313730643331333933303335333133383330333133333332333533373561313730643332333433303335333133363330333133333332333533373561333035663331323533303233303630333535303430333063316336353633363332643733366437303264363237323666366236353732326437333639363736653566353534333334326435303532346634343331313433303132303630333535303430623063306236393466353332303533373937333734363536643733333131333330313130363033353530343061306330613431373037303663363532303439366536333265333130623330303930363033353530343036313330323535353333303539333031333036303732613836343863653364303230313036303832613836343863653364303330313037303334323030303463323135373765646562643663376232323138663638646437303930613132313864633762306264366632633238336438343630393564393461663461353431316238333432306564383131663334303765383333333166316335346333663765623332323064366261643564346566663439323839383933653763306631336133383230323131333038323032306433303063303630333535316431333031303166663034303233303030333031663036303335353164323330343138333031363830313432336632343963343466393365346566323765366334663632383663336661326262666432653462333034353036303832623036303130353035303730313031303433393330333733303335303630383262303630313035303530373330303138363239363837343734373033613266326636663633373337303265363137303730366336353265363336663664326636663633373337303330333432643631373037303663363536313639363336313333333033323330383230313164303630333535316432303034383230313134333038323031313033303832303130633036303932613836343838366637363336343035303133303831666533303831633330363038326230363031303530353037303230323330383162363063383162333532363536633639363136653633363532303666366532303734363836393733323036333635373237343639363636393633363137343635323036323739323036313665373932303730363137323734373932303631373337333735366436353733323036313633363336353730373436313665363336353230366636363230373436383635323037343638363536653230363137303730366336393633363136323663363532303733373436313665363436313732363432303734363537323664373332303631366536343230363336663665363436393734363936663665373332303666363632303735373336353263323036333635373237343639363636393633363137343635323037303666366336393633373932303631366536343230363336353732373436393636363936333631373436393666366532303730373236313633373436393633363532303733373436313734363536643635366537343733326533303336303630383262303630313035303530373032303131363261363837343734373033613266326637373737373732653631373037303663363532653633366636643266363336353732373436393636363936333631373436353631373537343638366637323639373437393266333033343036303335353164316630343264333032623330323961303237613032353836323336383734373437303361326632663633373236633265363137303730366336353265363336663664326636313730373036633635363136393633363133333265363337323663333031643036303335353164306530343136303431343934353764623666643537343831383638393839373632663765353738353037653739623538323433303065303630333535316430663031303166663034303430333032303738303330306630363039326138363438383666373633363430363164303430323035303033303061303630383261383634386365336430343033303230333439303033303436303232313030626530393537316665373165316537333562353565356166616362346337326665623434356633303138353232326337323531303032623631656264366635353032323130306431386233353061356464366464366562313734363033356231316562326365383763666133653661663663626438333830383930646338326364646161363333303832303265653330383230323735613030333032303130323032303834393664326662663361393864613937333030613036303832613836343863653364303430333032333036373331316233303139303630333535303430333063313234313730373036633635323035323666366637343230343334313230326432303437333333313236333032343036303335353034306230633164343137303730366336353230343336353732373436393636363936333631373436393666366532303431373537343638366637323639373437393331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533333031653137306433313334333033353330333633323333333433363333333035613137306433323339333033353330333633323333333433363333333035613330376133313265333032633036303335353034303330633235343137303730366336353230343137303730366336393633363137343639366636653230343936653734363536373732363137343639366636653230343334313230326432303437333333313236333032343036303335353034306230633164343137303730366336353230343336353732373436393636363936333631373436393666366532303431373537343638366637323639373437393331313333303131303630333535303430613063306134313730373036633635323034393665363332653331306233303039303630333535303430363133303235353533333035393330313330363037326138363438636533643032303130363038326138363438636533643033303130373033343230303034663031373131383431396437363438356435316135653235383130373736653838306132656664653762616534646530386466633462393365313333353664353636356233356165323264303937373630643232346537626261303866643736313763653838636237366262363637306265633865383239383466663534343561333831663733303831663433303436303630383262303630313035303530373031303130343361333033383330333630363038326230363031303530353037333030313836326136383734373437303361326632663666363337333730326536313730373036633635326536333666366432663666363337333730333033343264363137303730366336353732366636663734363336313637333333303164303630333535316430653034313630343134323366323439633434663933653465663237653663346636323836633366613262626664326534623330306630363033353531643133303130316666303430353330303330313031666633303166303630333535316432333034313833303136383031346262623064656131353833333838396161343861393964656265626465626166646163623234616233303337303630333535316431663034333033303265333032636130326161303238383632363638373437343730336132663266363337323663326536313730373036633635326536333666366432663631373037303663363537323666366637343633363136373333326536333732366333303065303630333535316430663031303166663034303430333032303130363330313030363061326138363438383666373633363430363032306530343032303530303330306130363038326138363438636533643034303330323033363730303330363430323330336163663732383335313136393962313836666233356333353663613632626666343137656464393066373534646132386562656631396338313565343262373839663839386637396235393966393864353431306438663964653963326665303233303332326464353434323162306133303537373663356466333338336239303637666431373763326332313664393634666336373236393832313236663534663837613764316239396362396230393839323136313036393930663039393231643030303033313832303138643330383230313839303230313031333038313836333037613331326533303263303630333535303430333063323534313730373036633635323034313730373036633639363336313734363936663665323034393665373436353637373236313734363936663665323034333431323032643230343733333331323633303234303630333535303430623063316434313730373036633635323034333635373237343639363636393633363137343639366636653230343137353734363836663732363937343739333131333330313130363033353530343061306330613431373037303663363532303439366536333265333130623330303930363033353530343036313330323535353330323038346333303431343935313964353433363330306430363039363038363438303136353033303430323031303530306130383139353330313830363039326138363438383666373064303130393033333130623036303932613836343838366637306430313037303133303163303630393261383634383836663730643031303930353331306631373064333233313330333633303332333133373335333933313331356133303261303630393261383634383836663730643031303933343331316433303162333030643036303936303836343830313635303330343032303130353030613130613036303832613836343863653364303430333032333032663036303932613836343838366637306430313039303433313232303432306336396639373432383733363263303562396665616635646239663138373231633534383062643065373463373765363731326231636463616538346465363933303061303630383261383634386365336430343033303230343438333034363032323130306230383561316530323462323936323530323332363562363762356263376136353339393338653635633065326237366131343732363533366535336433393830323231303063666530333937626562623439623662336637386634346637386432336631343861663964326264383566633564386433326239663736316439326363316633303030303030303030303030227D';
10
+
11
+ // Development/sandbox domain verification content
12
+ const DEVELOPMENT_VERIFICATION =
13
+ '7B227073704964223A2231434145453235393330453932373535413231423330364239373833394330393833423031463145384543413646434531374532463439343146463246304231222C2276657273696F6E223A312C22637265617465644F6E223A313633393938323131323934372C227369676E6174757265223A223330383030363039326138363438383666373064303130373032613038303330383030323031303133313066333030643036303936303836343830313635303330343032303130353030333038303036303932613836343838366637306430313037303130303030613038303330383230336533333038323033383861303033303230313032303230383463333034313439353139643534333633303061303630383261383634386365336430343033303233303761333132653330326330363033353530343033306332353431373037303663363532303431373037303663363936333631373436393666366532303439366537343635363737323631373436393666366532303433343132303264323034373333333132363330323430363033353530343062306331643431373037303663363532303433363537323734363936363639363336313734363936663665323034313735373436383666373236393734373933313133333031313036303335353034306130633061343137303730366336353230343936653633326533313062333030393036303335353034303631333032353535333330316531373064333133393330333533313338333033313333333233353337356131373064333233343330333533313336333033313333333233353337356133303566333132353330323330363033353530343033306331633635363336333264373336643730326436323732366636623635373232643733363936373665356635353433333432643530353234663434333131343330313230363033353530343062306330623639343635333230353337393733373436353664373333313133333031313036303335353034306130633061343137303730366336353230343936653633326533313062333030393036303335353034303631333032353535333330353933303133303630373261383634386365336430323031303630383261383634386365336430333031303730333432303030346332313537376564656264366337623232313866363864643730393061313231386463376230626436663263323833643834363039356439346166346135343131623833343230656438313166333430376538333333316631633534633366376562333232306436626164356434656666343932383938393365376330663133613338323032313133303832303230643330306330363033353531643133303130316666303430323330303033303166303630333535316432333034313833303136383031343233663234396334346639336534656632376536633466363238366333666132626266643265346233303435303630383262303630313035303530373031303130343339333033373330333530363038326230363031303530353037333030313836323936383734373437303361326632663666363337333730326536313730373036633635326536333666366432663666363337333730333033343264363137303730366336353631363936333631333333303332333038323031316430363033353531643230303438323031313433303832303131303330383230313063303630393261383634383836663736333634303530313330383166653330383163333036303832623036303130353035303730323032333038316236306338316233353236353663363936313665363336353230366636653230373436383639373332303633363537323734363936363639363336313734363532303632373932303631366537393230373036313732373437393230363137333733373536643635373332303631363336333635373037343631366536333635323036663636323037343638363532303734363836353665323036313730373036633639363336313632366336353230373337343631366536343631373236343230373436353732366437333230363136653634323036333666366536343639373436393666366537333230366636363230373537333635326332303633363537323734363936363639363336313734363532303730366636633639363337393230363136653634323036333635373237343639363636393633363137343639366636653230373037323631363337343639363336353230373337343631373436353664363536653734373332653330333630363038326230363031303530353037303230313136326136383734373437303361326632663737373737373265363137303730366336353265363336663664326636333635373237343639363636393633363137343635363137353734363836663732363937343739326633303334303630333535316431663034326433303262333032396130323761303235383632333638373437343730336132663266363337323663326536313730373036633635326536333666366432663631373037303663363536313639363336313333326536333732366333303164303630333535316430653034313630343134393435376462366664353734383138363839383937363266376535373835303765373962353832343330306530363033353531643066303130316666303430343033303230373830333030663036303932613836343838366637363336343036316430343032303530303330306130363038326138363438636533643034303330323033343930303330343630323231303062653039353731666537316531653733356235356535616661636234633732666562343435663330313835323232633732353130303262363165626436663535303232313030643138623335306135646436646436656231373436303335623131656232636538376366613365366166366362643833383038393064633832636464616136333330383230326565333038323032373561303033303230313032303230383439366432666266336139386461393733303061303630383261383634386365336430343033303233303637333131623330313930363033353530343033306331323431373037303663363532303532366636663734323034333431323032643230343733333331323633303234303630333535303430623063316434313730373036633635323034333635373237343639363636393633363137343639366636653230343137353734363836663732363937343739333131333330313130363033353530343061306330613431373037303663363532303439366536333265333130623330303930363033353530343036313330323535353333303165313730643331333433303335333033363332333333343336333333303561313730643332333933303335333033363332333333343336333333303561333037613331326533303263303630333535303430333063323534313730373036633635323034313730373036633639363336313734363936663665323034393665373436353637373236313734363936663665323034333431323032643230343733333331323633303234303630333535303430623063316434313730373036633635323034333635373237343639363636393633363137343639366636653230343137353734363836663732363937343739333131333330313130363033353530343061306330613431373037303663363532303439366536333265333130623330303930363033353530343036313330323535353333303539333031333036303732613836343863653364303230313036303832613836343863653364303330313037303334323030303466303137313138343139643736343835643531613565323538313037373665383830613265666465376261653464653038646663346239336531333335366435363635623335616532326430393737363064323234653762626130386664373631376365383863623736626236363730626563386538323938346666353434356133383166373330383166343330343630363038326230363031303530353037303130313034336133303338333033363036303832623036303130353035303733303031383632613638373437343730336132663266366636333733373032653631373037303663363532653633366636643266366636333733373033303334326436313730373036633635373236663666373436333631363733333330316430363033353531643065303431363034313432336632343963343466393365346566323765366334663632383663336661326262666432653462333030663036303335353164313330313031666630343035333030333031303166663330316630363033353531643233303431383330313638303134626262306465613135383333383839616134386139396465626562646562616664616362323461623330333730363033353531643166303433303330326533303263613032616130323838363236363837343734373033613266326636333732366332653631373037303663363532653633366636643266363137303730366336353732366636663734363336313637333332653633373236633330306530363033353531643066303130316666303430343033303230313036333031303036306132613836343838366637363336343036303230653034303230353030333030613036303832613836343863653364303430333032303336373030333036343032333033616366373238333531313639396231383666623335633335366361363262666634313765646439306637353464613238656265663139633831356534326237383966383938663739623539396639386435343130643866396465396332666530323330333232646435343432316230613330353737366335646633333833623930363766643137376332633231366439363466633637323639383231323666353466383761376431623939636239623039383932313631303639393066303939323164303030303331383230313863333038323031383830323031303133303831383633303761333132653330326330363033353530343033306332353431373037303663363532303431373037303663363936333631373436393666366532303439366537343635363737323631373436393666366532303433343132303264323034373333333132363330323430363033353530343062306331643431373037303633363532303433363537323734363936363639363336313734363936663665323034313735373436383666373236393734373933313133333031313036303335353034306130633061343137303730366336353230343936653633326533313062333030393036303335353034303631333032353535333032303834633330343134393531396435343336333030643036303936303836343830313635303330343032303130353030613038313935333031383036303932613836343838366637306430313039303333313062303630393261383634383836663730643031303730313330316330363039326138363438383666373064303130393035333130663137306433323331333133323332333033303336333333353331333235613330326130363039326138363438383666373064303130393334333131643330316233303064303630393630383634383031363530333034303230313035303061313061303630383261383634386365336430343033303233303266303630393261383634383836663730643031303930343331323230343230326531313362373864353534386366376263393961306435663330663066653861376538326639363961613039303963343939366537306432386230356537663330306130363038326138363438636533643034303330323034343733303435303232313030633965663438663437393162623732656565646337386564633562306133376633383863623564326262366337653766323762646461646462306564353438623032323030333265316537356363653432303334323666393363656338323666663738383833636564326635343463326232326266363266343961363731396464303362303030303030303030303030227D';
14
+
15
+ export async function GET() {
16
+ const isDev = process.env.NODE_ENV === 'development';
17
+ const content = isDev ? DEVELOPMENT_VERIFICATION : PRODUCTION_VERIFICATION;
18
+
19
+ return new NextResponse(content, {
20
+ status: 200,
21
+ headers: {
22
+ 'Content-Type': 'text/plain',
23
+ 'Cache-Control': 'public, max-age=86400',
24
+ },
25
+ });
26
+ }
@@ -9,6 +9,7 @@ import type {
9
9
  ShippingRate,
10
10
  SetShippingAddressDto,
11
11
  ShippingDestinations,
12
+ PickupLocation,
12
13
  } from 'brainerce';
13
14
  import { formatPrice } from 'brainerce';
14
15
  import { getClient } from '@/lib/brainerce';
@@ -16,12 +17,14 @@ import { useStoreInfo, useAuth, useCart } from '@/providers/store-provider';
16
17
  import { CheckoutForm } from '@/components/checkout/checkout-form';
17
18
  import { ShippingStep } from '@/components/checkout/shipping-step';
18
19
  import { PaymentStep } from '@/components/checkout/payment-step';
20
+ import { DeliveryMethodStep } from '@/components/checkout/delivery-method-step';
21
+ import { PickupStep } from '@/components/checkout/pickup-step';
19
22
  import { TaxDisplay } from '@/components/checkout/tax-display';
20
23
  import { ReservationCountdown } from '@/components/cart/reservation-countdown';
21
24
  import { LoadingSpinner } from '@/components/shared/loading-spinner';
22
25
  import { cn } from '@/lib/utils';
23
26
 
24
- type CheckoutStep = 'address' | 'shipping' | 'payment';
27
+ type CheckoutStep = 'method' | 'address' | 'shipping' | 'pickup' | 'payment';
25
28
 
26
29
  function CheckoutContent() {
27
30
  const searchParams = useSearchParams();
@@ -38,6 +41,8 @@ function CheckoutContent() {
38
41
  const [initializing, setInitializing] = useState(true);
39
42
  const [error, setError] = useState<string | null>(null);
40
43
  const [destinations, setDestinations] = useState<ShippingDestinations | null>(null);
44
+ const [pickupLocations, setPickupLocations] = useState<PickupLocation[]>([]);
45
+ const [deliveryType, setDeliveryType] = useState<'shipping' | 'pickup'>('shipping');
41
46
 
42
47
  // Check for returning from canceled payment
43
48
  const canceled = searchParams.get('canceled') === 'true';
@@ -50,25 +55,33 @@ function CheckoutContent() {
50
55
  setError(null);
51
56
  const client = getClient();
52
57
 
53
- // Fetch shipping destinations for country/region dropdowns
58
+ // Fetch shipping destinations and pickup locations in parallel
54
59
  client
55
60
  .getShippingDestinations()
56
61
  .then(setDestinations)
57
62
  .catch(() => {});
58
63
 
64
+ const locations = await client.getPickupLocations().catch(() => [] as PickupLocation[]);
65
+ setPickupLocations(locations);
66
+
59
67
  // If returning with existing checkout ID, resume it
60
68
  if (existingCheckoutId) {
61
69
  const existing = await client.getCheckout(existingCheckoutId);
62
70
  setCheckout(existing);
63
71
 
64
72
  // Determine step based on checkout state
65
- if (existing.shippingAddress && existing.shippingRateId) {
73
+ if (existing.deliveryType === 'pickup' && existing.pickupLocation) {
74
+ setDeliveryType('pickup');
75
+ setStep('payment');
76
+ } else if (existing.shippingAddress && existing.shippingRateId) {
66
77
  setStep('payment');
67
78
  } else if (existing.shippingAddress) {
68
79
  // Fetch shipping rates
69
80
  const rates = await client.getShippingRates(existing.id);
70
81
  setShippingRates(rates);
71
82
  setStep('shipping');
83
+ } else if (locations.length > 0) {
84
+ setStep('method');
72
85
  }
73
86
  return;
74
87
  }
@@ -92,6 +105,11 @@ function CheckoutContent() {
92
105
  } else {
93
106
  setError('Your cart is empty.');
94
107
  }
108
+
109
+ // If pickup locations exist, start with delivery method selection
110
+ if (locations.length > 0) {
111
+ setStep('method');
112
+ }
95
113
  } catch (err) {
96
114
  const message = err instanceof Error ? err.message : 'Failed to initialize checkout';
97
115
  setError(message);
@@ -149,6 +167,60 @@ function CheckoutContent() {
149
167
  }
150
168
  }
151
169
 
170
+ // Handle delivery method selection
171
+ async function handleDeliveryTypeSelect(method: 'shipping' | 'pickup') {
172
+ if (!checkout) return;
173
+
174
+ try {
175
+ setLoading(true);
176
+ setError(null);
177
+ setDeliveryType(method);
178
+ const client = getClient();
179
+
180
+ await client.setDeliveryType(checkout.id, method);
181
+
182
+ if (method === 'shipping') {
183
+ setStep('address');
184
+ } else {
185
+ setStep('pickup');
186
+ }
187
+ } catch (err) {
188
+ const message = err instanceof Error ? err.message : 'Failed to set delivery method';
189
+ setError(message);
190
+ } finally {
191
+ setLoading(false);
192
+ }
193
+ }
194
+
195
+ // Handle pickup location selection
196
+ async function handlePickupSelect(
197
+ locationId: string,
198
+ customerInfo: { email: string; firstName?: string; lastName?: string; phone?: string }
199
+ ) {
200
+ if (!checkout) return;
201
+
202
+ try {
203
+ setLoading(true);
204
+ setError(null);
205
+ const client = getClient();
206
+
207
+ const updated = await client.selectPickupLocation(checkout.id, {
208
+ pickupRateId: locationId,
209
+ email: customerInfo.email,
210
+ firstName: customerInfo.firstName,
211
+ lastName: customerInfo.lastName,
212
+ phone: customerInfo.phone,
213
+ });
214
+ setCheckout(updated);
215
+ setStep('payment');
216
+ } catch (err) {
217
+ const message = err instanceof Error ? err.message : 'Failed to select pickup location';
218
+ setError(message);
219
+ } finally {
220
+ setLoading(false);
221
+ }
222
+ }
223
+
152
224
  if (initializing) {
153
225
  return (
154
226
  <div className="flex min-h-[60vh] items-center justify-center">
@@ -190,11 +262,25 @@ function CheckoutContent() {
190
262
  );
191
263
  }
192
264
 
193
- const steps: { key: CheckoutStep; label: string; number: number }[] = [
194
- { key: 'address', label: 'Address', number: 1 },
195
- { key: 'shipping', label: 'Shipping', number: 2 },
196
- { key: 'payment', label: 'Payment', number: 3 },
197
- ];
265
+ const steps: { key: CheckoutStep; label: string }[] =
266
+ pickupLocations.length > 0
267
+ ? deliveryType === 'pickup'
268
+ ? [
269
+ { key: 'method', label: 'Method' },
270
+ { key: 'pickup', label: 'Pickup' },
271
+ { key: 'payment', label: 'Payment' },
272
+ ]
273
+ : [
274
+ { key: 'method', label: 'Method' },
275
+ { key: 'address', label: 'Address' },
276
+ { key: 'shipping', label: 'Shipping' },
277
+ { key: 'payment', label: 'Payment' },
278
+ ]
279
+ : [
280
+ { key: 'address', label: 'Address' },
281
+ { key: 'shipping', label: 'Shipping' },
282
+ { key: 'payment', label: 'Payment' },
283
+ ];
198
284
 
199
285
  const currentStepIndex = steps.findIndex((s) => s.key === step);
200
286
 
@@ -247,7 +333,7 @@ function CheckoutContent() {
247
333
  />
248
334
  </svg>
249
335
  ) : (
250
- s.number
336
+ index + 1
251
337
  )}
252
338
  </div>
253
339
  <span
@@ -275,10 +361,29 @@ function CheckoutContent() {
275
361
  <div className="grid grid-cols-1 gap-8 lg:grid-cols-3">
276
362
  {/* Main content */}
277
363
  <div className="lg:col-span-2">
278
- {/* Step 1: Address */}
364
+ {/* Delivery Method */}
365
+ {step === 'method' && (
366
+ <div>
367
+ <h2 className="text-foreground mb-4 text-lg font-semibold">Delivery Method</h2>
368
+ <DeliveryMethodStep onSelect={handleDeliveryTypeSelect} />
369
+ </div>
370
+ )}
371
+
372
+ {/* Address */}
279
373
  {step === 'address' && (
280
374
  <div>
281
- <h2 className="text-foreground mb-4 text-lg font-semibold">Shipping Address</h2>
375
+ <div className="mb-4 flex items-center justify-between">
376
+ <h2 className="text-foreground text-lg font-semibold">Shipping Address</h2>
377
+ {pickupLocations.length > 0 && (
378
+ <button
379
+ type="button"
380
+ onClick={() => setStep('method')}
381
+ className="text-primary text-sm hover:underline"
382
+ >
383
+ Change method
384
+ </button>
385
+ )}
386
+ </div>
282
387
  <CheckoutForm
283
388
  onSubmit={handleAddressSubmit}
284
389
  loading={loading}
@@ -326,17 +431,39 @@ function CheckoutContent() {
326
431
  </div>
327
432
  )}
328
433
 
329
- {/* Step 3: Payment */}
434
+ {/* Pickup */}
435
+ {step === 'pickup' && (
436
+ <div>
437
+ <div className="mb-4 flex items-center justify-between">
438
+ <h2 className="text-foreground text-lg font-semibold">Pickup Location</h2>
439
+ <button
440
+ type="button"
441
+ onClick={() => setStep('method')}
442
+ className="text-primary text-sm hover:underline"
443
+ >
444
+ Change method
445
+ </button>
446
+ </div>
447
+ <PickupStep
448
+ locations={pickupLocations}
449
+ onSelect={handlePickupSelect}
450
+ loading={loading}
451
+ initialEmail={checkout?.email || ''}
452
+ />
453
+ </div>
454
+ )}
455
+
456
+ {/* Payment */}
330
457
  {step === 'payment' && checkout && (
331
458
  <div>
332
459
  <div className="mb-4 flex items-center justify-between">
333
460
  <h2 className="text-foreground text-lg font-semibold">Payment</h2>
334
461
  <button
335
462
  type="button"
336
- onClick={() => setStep('shipping')}
463
+ onClick={() => setStep(deliveryType === 'pickup' ? 'pickup' : 'shipping')}
337
464
  className="text-primary text-sm hover:underline"
338
465
  >
339
- Change shipping
466
+ {deliveryType === 'pickup' ? 'Change pickup' : 'Change shipping'}
340
467
  </button>
341
468
  </div>
342
469
 
@@ -430,11 +557,18 @@ function CheckoutContent() {
430
557
  </div>
431
558
  )}
432
559
 
433
- {parseFloat(checkout.shippingAmount) > 0 && (
560
+ {(parseFloat(checkout.shippingAmount) > 0 ||
561
+ checkout.deliveryType === 'pickup') && (
434
562
  <div className="flex items-center justify-between">
435
- <span className="text-muted-foreground">Shipping</span>
563
+ <span className="text-muted-foreground">
564
+ {checkout.deliveryType === 'pickup' ? 'Pickup' : 'Shipping'}
565
+ </span>
436
566
  <span className="text-foreground">
437
- {formatPrice(parseFloat(checkout.shippingAmount), { currency }) as string}
567
+ {parseFloat(checkout.shippingAmount) === 0
568
+ ? 'Free'
569
+ : (formatPrice(parseFloat(checkout.shippingAmount), {
570
+ currency,
571
+ }) as string)}
438
572
  </span>
439
573
  </div>
440
574
  )}