create-cartbase 0.1.16 → 0.1.18
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/LICENSE +21 -21
- package/README.md +25 -25
- package/dist/index.js +20 -20
- package/package.json +1 -1
- package/template/app/docs/auth.md +105 -105
- package/template/app/docs/carts.md +376 -376
- package/template/app/docs/categories.md +194 -194
- package/template/app/docs/checkout.md +714 -714
- package/template/app/docs/components.md +55 -11
- package/template/app/docs/consent.md +91 -91
- package/template/app/docs/deploy.md +197 -197
- package/template/app/docs/gift-cards.md +153 -153
- package/template/app/docs/metaobjects.md +126 -126
- package/template/app/docs/orders.md +221 -221
- package/template/app/docs/products.md +51 -2
- package/template/app/docs/regions.md +269 -269
- package/template/app/docs/reviews.md +223 -223
- package/template/app/docs/search.md +227 -227
- package/template/app/docs/store.md +47 -47
- package/template/app/docs/subscriptions.md +148 -148
- package/template/app/docs/variables.md +315 -315
- package/template/app/package.json +1 -1
- package/template/app/postcss.config.cjs +11 -11
- package/template/app/src/app/checkout/checkout-page-client.tsx +73 -73
- package/template/app/src/app/checkout/page.tsx +48 -48
- package/template/app/src/app/globals.css +26 -26
- package/template/app/src/app/page.tsx +28 -28
- package/template/app/src/app/products/[handle]/page.tsx +87 -87
- package/template/app/src/app/providers.tsx +68 -64
- package/template/app/src/app/search/page.tsx +23 -23
- package/template/app/src/lib/browser-client.ts +35 -35
- package/template/app/src/lib/config.ts +41 -41
- package/template/app/src/lib/server-client.ts +25 -25
- package/template/app/src/lib/cart-actions.ts +0 -47
|
@@ -1,64 +1,68 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import type { Cart } from "@cartbase/storefront/api/carts"
|
|
4
|
-
import { CartDrawerProvider } from "@cartbase/storefront/cart-drawer/context"
|
|
5
|
-
import { CartDrawerTemplate } from "@cartbase/storefront/cart-drawer/template"
|
|
6
|
-
import { ConsentBanner } from "@cartbase/storefront/tracking/consent-banner"
|
|
7
|
-
import {
|
|
8
|
-
pickConsentCopy,
|
|
9
|
-
shouldRenderBanner,
|
|
10
|
-
type ConsentSettings,
|
|
11
|
-
} from "@cartbase/storefront/tracking/consent"
|
|
12
|
-
import { StorefrontLocaleProvider } from "@cartbase/storefront/locales"
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
{
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import type { Cart } from "@cartbase/storefront/api/carts"
|
|
4
|
+
import { CartDrawerProvider } from "@cartbase/storefront/cart-drawer/context"
|
|
5
|
+
import { CartDrawerTemplate } from "@cartbase/storefront/cart-drawer/template"
|
|
6
|
+
import { ConsentBanner } from "@cartbase/storefront/tracking/consent-banner"
|
|
7
|
+
import {
|
|
8
|
+
pickConsentCopy,
|
|
9
|
+
shouldRenderBanner,
|
|
10
|
+
type ConsentSettings,
|
|
11
|
+
} from "@cartbase/storefront/tracking/consent"
|
|
12
|
+
import { StorefrontLocaleProvider } from "@cartbase/storefront/locales"
|
|
13
|
+
import { clearCartCookie } from "@cartbase/storefront/lib/cookie-names"
|
|
14
|
+
import { CART_COOKIE, CART_COOKIE_MAX_AGE, STORE_LOCALE } from "@/lib/config"
|
|
15
|
+
import { browserClient } from "@/lib/browser-client"
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Client-side shell: the store's language, ONE CartDrawerProvider per app
|
|
19
|
+
* (cart-drawer family mount rule) + the consent banner (consent.md: render
|
|
20
|
+
* the builtin banner only when `enabled && mode === "builtin"`; ConsentInit
|
|
21
|
+
* stays in the server layout as the first child of <body>).
|
|
22
|
+
*
|
|
23
|
+
* The language is mounted once, here. Every client component inside reads
|
|
24
|
+
* it, the cart drawer included, so nothing is handed over below. The
|
|
25
|
+
* server-rendered templates cannot read it, so each page hands them their
|
|
26
|
+
* area (`labels={STORE_LOCALE.store}`), and those props are required.
|
|
27
|
+
*/
|
|
28
|
+
export function Providers({
|
|
29
|
+
cart,
|
|
30
|
+
consent,
|
|
31
|
+
children,
|
|
32
|
+
}: {
|
|
33
|
+
cart: Cart | null
|
|
34
|
+
consent: ConsentSettings
|
|
35
|
+
children: React.ReactNode
|
|
36
|
+
}) {
|
|
37
|
+
// The store's own consent copy, in the store's language where it has it;
|
|
38
|
+
// pickConsentCopy falls back to English and then to whatever exists.
|
|
39
|
+
const copy = pickConsentCopy(consent.copy, STORE_LOCALE.code)
|
|
40
|
+
return (
|
|
41
|
+
<StorefrontLocaleProvider locale={STORE_LOCALE}>
|
|
42
|
+
<CartDrawerProvider
|
|
43
|
+
cart={cart}
|
|
44
|
+
client={browserClient}
|
|
45
|
+
onCartChange={(next) => {
|
|
46
|
+
// Persist the cart id (carts.md: the app owns the cart cookie).
|
|
47
|
+
document.cookie = `${CART_COOKIE}=${encodeURIComponent(
|
|
48
|
+
next.id
|
|
49
|
+
)};path=/;max-age=${CART_COOKIE_MAX_AGE};samesite=lax`
|
|
50
|
+
}}
|
|
51
|
+
// The cart is over (ordered, or gone): forget the stored id, so the
|
|
52
|
+
// next add starts a new cart.
|
|
53
|
+
onCartEnd={clearCartCookie}
|
|
54
|
+
>
|
|
55
|
+
{children}
|
|
56
|
+
<CartDrawerTemplate />
|
|
57
|
+
{shouldRenderBanner(consent) && copy && (
|
|
58
|
+
<ConsentBanner
|
|
59
|
+
copy={copy}
|
|
60
|
+
layout={consent.layout}
|
|
61
|
+
privacyHref={consent.privacy_href}
|
|
62
|
+
rejectOnFirstLayer={consent.reject_on_first_layer}
|
|
63
|
+
/>
|
|
64
|
+
)}
|
|
65
|
+
</CartDrawerProvider>
|
|
66
|
+
</StorefrontLocaleProvider>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { SearchTemplate } from "@cartbase/storefront/store/search-template"
|
|
2
|
-
import { PRICING_CONTEXT, STORE_LOCALE } from "@/lib/config"
|
|
3
|
-
import { getServerClient } from "@/lib/server-client"
|
|
4
|
-
|
|
5
|
-
/** Search page (runbook step 5) — fully URL-state driven search-template. */
|
|
6
|
-
export default async function SearchPage({
|
|
7
|
-
searchParams,
|
|
8
|
-
}: {
|
|
9
|
-
searchParams: Promise<Record<string, string | string[] | undefined>>
|
|
10
|
-
}) {
|
|
11
|
-
const params = await searchParams
|
|
12
|
-
const client = await getServerClient()
|
|
13
|
-
return (
|
|
14
|
-
<SearchTemplate
|
|
15
|
-
client={client}
|
|
16
|
-
searchParams={params}
|
|
17
|
-
pricingContext={PRICING_CONTEXT}
|
|
18
|
-
// Server-rendered: the locale provider cannot reach it, so the pack
|
|
19
|
-
// comes as a prop, and the prop is required.
|
|
20
|
-
labels={STORE_LOCALE.store}
|
|
21
|
-
/>
|
|
22
|
-
)
|
|
23
|
-
}
|
|
1
|
+
import { SearchTemplate } from "@cartbase/storefront/store/search-template"
|
|
2
|
+
import { PRICING_CONTEXT, STORE_LOCALE } from "@/lib/config"
|
|
3
|
+
import { getServerClient } from "@/lib/server-client"
|
|
4
|
+
|
|
5
|
+
/** Search page (runbook step 5) — fully URL-state driven search-template. */
|
|
6
|
+
export default async function SearchPage({
|
|
7
|
+
searchParams,
|
|
8
|
+
}: {
|
|
9
|
+
searchParams: Promise<Record<string, string | string[] | undefined>>
|
|
10
|
+
}) {
|
|
11
|
+
const params = await searchParams
|
|
12
|
+
const client = await getServerClient()
|
|
13
|
+
return (
|
|
14
|
+
<SearchTemplate
|
|
15
|
+
client={client}
|
|
16
|
+
searchParams={params}
|
|
17
|
+
pricingContext={PRICING_CONTEXT}
|
|
18
|
+
// Server-rendered: the locale provider cannot reach it, so the pack
|
|
19
|
+
// comes as a prop, and the prop is required.
|
|
20
|
+
labels={STORE_LOCALE.store}
|
|
21
|
+
/>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import { StorefrontClient } from "@cartbase/storefront/api/http"
|
|
4
|
-
import {
|
|
5
|
-
BARTER_CLIENT_ID,
|
|
6
|
-
BARTER_PUBLISHABLE_KEY,
|
|
7
|
-
BARTER_URL,
|
|
8
|
-
LOCALE_COOKIE,
|
|
9
|
-
} from "./config"
|
|
10
|
-
|
|
11
|
-
function readCookie(name: string): string | null {
|
|
12
|
-
if (typeof document === "undefined") return null
|
|
13
|
-
const match = document.cookie
|
|
14
|
-
.split("; ")
|
|
15
|
-
.find((row) => row.startsWith(`${name}=`))
|
|
16
|
-
return match ? decodeURIComponent(match.slice(name.length + 1)) : null
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Browser-scope StorefrontClient (runbook step 2): constructed once per
|
|
21
|
-
* app. Guest-only reference app — no auth token store.
|
|
22
|
-
*
|
|
23
|
-
* baseUrl is the APP origin, not the API origin: the store API ships no
|
|
24
|
-
* CORS headers, so browser calls ride the same-origin `/api/store/*`
|
|
25
|
-
* rewrite (next.config.ts). During SSR of client components no fetches
|
|
26
|
-
* run — the API origin stands in only to satisfy the constructor.
|
|
27
|
-
*/
|
|
28
|
-
export const browserClient = new StorefrontClient({
|
|
29
|
-
baseUrl:
|
|
30
|
-
typeof window !== "undefined" ? window.location.origin : BARTER_URL,
|
|
31
|
-
clientId: BARTER_CLIENT_ID,
|
|
32
|
-
publishableKey: BARTER_PUBLISHABLE_KEY,
|
|
33
|
-
getAuthToken: () => null,
|
|
34
|
-
getLocale: () => readCookie(LOCALE_COOKIE),
|
|
35
|
-
})
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { StorefrontClient } from "@cartbase/storefront/api/http"
|
|
4
|
+
import {
|
|
5
|
+
BARTER_CLIENT_ID,
|
|
6
|
+
BARTER_PUBLISHABLE_KEY,
|
|
7
|
+
BARTER_URL,
|
|
8
|
+
LOCALE_COOKIE,
|
|
9
|
+
} from "./config"
|
|
10
|
+
|
|
11
|
+
function readCookie(name: string): string | null {
|
|
12
|
+
if (typeof document === "undefined") return null
|
|
13
|
+
const match = document.cookie
|
|
14
|
+
.split("; ")
|
|
15
|
+
.find((row) => row.startsWith(`${name}=`))
|
|
16
|
+
return match ? decodeURIComponent(match.slice(name.length + 1)) : null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Browser-scope StorefrontClient (runbook step 2): constructed once per
|
|
21
|
+
* app. Guest-only reference app — no auth token store.
|
|
22
|
+
*
|
|
23
|
+
* baseUrl is the APP origin, not the API origin: the store API ships no
|
|
24
|
+
* CORS headers, so browser calls ride the same-origin `/api/store/*`
|
|
25
|
+
* rewrite (next.config.ts). During SSR of client components no fetches
|
|
26
|
+
* run — the API origin stands in only to satisfy the constructor.
|
|
27
|
+
*/
|
|
28
|
+
export const browserClient = new StorefrontClient({
|
|
29
|
+
baseUrl:
|
|
30
|
+
typeof window !== "undefined" ? window.location.origin : BARTER_URL,
|
|
31
|
+
clientId: BARTER_CLIENT_ID,
|
|
32
|
+
publishableKey: BARTER_PUBLISHABLE_KEY,
|
|
33
|
+
getAuthToken: () => null,
|
|
34
|
+
getLocale: () => readCookie(LOCALE_COOKIE),
|
|
35
|
+
})
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
/** Shared storefront configuration (BUILD-A-STOREFRONT.md — the one input). */
|
|
2
|
-
|
|
3
|
-
import { cartbaseApiOrigin } from "@cartbase/storefront/lib/platform"
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* The store's publishable key names the store and scopes its catalog: the
|
|
7
|
-
* ONE value a storefront needs (one key, 2026-09-07). The origin is the
|
|
8
|
-
* platform's, overridable for a local or staging platform; the client id is
|
|
9
|
-
* the platform's own door (hosted builds carry it) and never something to
|
|
10
|
-
* copy by hand.
|
|
11
|
-
*/
|
|
12
|
-
export const BARTER_PUBLISHABLE_KEY =
|
|
13
|
-
process.env.NEXT_PUBLIC_CARTBASE_PUBLISHABLE_KEY || undefined
|
|
14
|
-
export const BARTER_URL = cartbaseApiOrigin(process.env.NEXT_PUBLIC_CARTBASE_URL)
|
|
15
|
-
export const BARTER_CLIENT_ID = process.env.NEXT_PUBLIC_CARTBASE_CLIENT_ID || undefined
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Cart-id cookie — owned by the app (the SDK never persists the cart);
|
|
19
|
-
* the NAME is owned by the library so every consumer emits the same wire
|
|
20
|
-
* fingerprint (platform-fingerprints card). `readCartCookie` is the
|
|
21
|
-
* backward-compat reader for the legacy `_barter_cart_id` name.
|
|
22
|
-
*/
|
|
23
|
-
export { CART_COOKIE, readCartCookie } from "@cartbase/storefront/lib/cookie-names"
|
|
24
|
-
export const CART_COOKIE_MAX_AGE = 60 * 60 * 24 * 30 // 30 days
|
|
25
|
-
|
|
26
|
-
/** Locale cookie read by the clients' `getLocale`. */
|
|
27
|
-
export const LOCALE_COOKIE = "_barter_locale"
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* THE STORE'S LANGUAGE, declared once. The library speaks international
|
|
31
|
-
* English and knows no other language; a language is a typed pack the store
|
|
32
|
-
* imports. Swap `en` for `bg` or `es` here (`@cartbase/storefront/locales/bg`)
|
|
33
|
-
* and every screen follows: providers.tsx mounts it for the client
|
|
34
|
-
* components, and each page hands the server-rendered templates their area
|
|
35
|
-
* from it (`labels={STORE_LOCALE.store}`), because a server component cannot
|
|
36
|
-
* read a client context. Those props are required, so a page cannot forget.
|
|
37
|
-
*/
|
|
38
|
-
export { en as STORE_LOCALE } from "@cartbase/storefront/locales/en"
|
|
39
|
-
|
|
40
|
-
/** Pricing context for every catalog surface (runbook step 5). EUR only. */
|
|
41
|
-
export const PRICING_CONTEXT = { currency_code: "eur" } as const
|
|
1
|
+
/** Shared storefront configuration (BUILD-A-STOREFRONT.md — the one input). */
|
|
2
|
+
|
|
3
|
+
import { cartbaseApiOrigin } from "@cartbase/storefront/lib/platform"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The store's publishable key names the store and scopes its catalog: the
|
|
7
|
+
* ONE value a storefront needs (one key, 2026-09-07). The origin is the
|
|
8
|
+
* platform's, overridable for a local or staging platform; the client id is
|
|
9
|
+
* the platform's own door (hosted builds carry it) and never something to
|
|
10
|
+
* copy by hand.
|
|
11
|
+
*/
|
|
12
|
+
export const BARTER_PUBLISHABLE_KEY =
|
|
13
|
+
process.env.NEXT_PUBLIC_CARTBASE_PUBLISHABLE_KEY || undefined
|
|
14
|
+
export const BARTER_URL = cartbaseApiOrigin(process.env.NEXT_PUBLIC_CARTBASE_URL)
|
|
15
|
+
export const BARTER_CLIENT_ID = process.env.NEXT_PUBLIC_CARTBASE_CLIENT_ID || undefined
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Cart-id cookie — owned by the app (the SDK never persists the cart);
|
|
19
|
+
* the NAME is owned by the library so every consumer emits the same wire
|
|
20
|
+
* fingerprint (platform-fingerprints card). `readCartCookie` is the
|
|
21
|
+
* backward-compat reader for the legacy `_barter_cart_id` name.
|
|
22
|
+
*/
|
|
23
|
+
export { CART_COOKIE, readCartCookie } from "@cartbase/storefront/lib/cookie-names"
|
|
24
|
+
export const CART_COOKIE_MAX_AGE = 60 * 60 * 24 * 30 // 30 days
|
|
25
|
+
|
|
26
|
+
/** Locale cookie read by the clients' `getLocale`. */
|
|
27
|
+
export const LOCALE_COOKIE = "_barter_locale"
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* THE STORE'S LANGUAGE, declared once. The library speaks international
|
|
31
|
+
* English and knows no other language; a language is a typed pack the store
|
|
32
|
+
* imports. Swap `en` for `bg` or `es` here (`@cartbase/storefront/locales/bg`)
|
|
33
|
+
* and every screen follows: providers.tsx mounts it for the client
|
|
34
|
+
* components, and each page hands the server-rendered templates their area
|
|
35
|
+
* from it (`labels={STORE_LOCALE.store}`), because a server component cannot
|
|
36
|
+
* read a client context. Those props are required, so a page cannot forget.
|
|
37
|
+
*/
|
|
38
|
+
export { en as STORE_LOCALE } from "@cartbase/storefront/locales/en"
|
|
39
|
+
|
|
40
|
+
/** Pricing context for every catalog surface (runbook step 5). EUR only. */
|
|
41
|
+
export const PRICING_CONTEXT = { currency_code: "eur" } as const
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import { cookies } from "next/headers"
|
|
2
|
-
import { StorefrontClient } from "@cartbase/storefront/api/http"
|
|
3
|
-
import {
|
|
4
|
-
BARTER_CLIENT_ID,
|
|
5
|
-
BARTER_PUBLISHABLE_KEY,
|
|
6
|
-
BARTER_URL,
|
|
7
|
-
LOCALE_COOKIE,
|
|
8
|
-
} from "./config"
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Server-scope StorefrontClient (runbook step 2): construct per request;
|
|
12
|
-
* `getLocale` reads the locale cookie. No customer accounts in this
|
|
13
|
-
* reference app, so `getAuthToken` always answers null (guest).
|
|
14
|
-
*/
|
|
15
|
-
export async function getServerClient(): Promise<StorefrontClient> {
|
|
16
|
-
const jar = await cookies()
|
|
17
|
-
const locale = jar.get(LOCALE_COOKIE)?.value ?? null
|
|
18
|
-
return new StorefrontClient({
|
|
19
|
-
baseUrl: BARTER_URL,
|
|
20
|
-
clientId: BARTER_CLIENT_ID,
|
|
21
|
-
publishableKey: BARTER_PUBLISHABLE_KEY,
|
|
22
|
-
getAuthToken: () => null,
|
|
23
|
-
getLocale: () => locale,
|
|
24
|
-
})
|
|
25
|
-
}
|
|
1
|
+
import { cookies } from "next/headers"
|
|
2
|
+
import { StorefrontClient } from "@cartbase/storefront/api/http"
|
|
3
|
+
import {
|
|
4
|
+
BARTER_CLIENT_ID,
|
|
5
|
+
BARTER_PUBLISHABLE_KEY,
|
|
6
|
+
BARTER_URL,
|
|
7
|
+
LOCALE_COOKIE,
|
|
8
|
+
} from "./config"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Server-scope StorefrontClient (runbook step 2): construct per request;
|
|
12
|
+
* `getLocale` reads the locale cookie. No customer accounts in this
|
|
13
|
+
* reference app, so `getAuthToken` always answers null (guest).
|
|
14
|
+
*/
|
|
15
|
+
export async function getServerClient(): Promise<StorefrontClient> {
|
|
16
|
+
const jar = await cookies()
|
|
17
|
+
const locale = jar.get(LOCALE_COOKIE)?.value ?? null
|
|
18
|
+
return new StorefrontClient({
|
|
19
|
+
baseUrl: BARTER_URL,
|
|
20
|
+
clientId: BARTER_CLIENT_ID,
|
|
21
|
+
publishableKey: BARTER_PUBLISHABLE_KEY,
|
|
22
|
+
getAuthToken: () => null,
|
|
23
|
+
getLocale: () => locale,
|
|
24
|
+
})
|
|
25
|
+
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
"use server"
|
|
2
|
-
|
|
3
|
-
import { revalidatePath } from "next/cache"
|
|
4
|
-
import { cookies } from "next/headers"
|
|
5
|
-
import { addLineItem, createCart } from "@cartbase/storefront/api/carts"
|
|
6
|
-
import { CART_COOKIE, CART_COOKIE_MAX_AGE, readCartCookie } from "./config"
|
|
7
|
-
import { getServerClient } from "./server-client"
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* PDP add-to-cart server action (carts.md: create the cart lazily on the
|
|
11
|
-
* first add — region falls back to the store default; persist `cart.id`
|
|
12
|
-
* in a cookie). Passed into `ProductTemplate`'s `addToCart` seam.
|
|
13
|
-
*
|
|
14
|
-
* Reads via `readCartCookie` (new `_cartbase_cart` name, falls back to the
|
|
15
|
-
* legacy `_barter_cart_id` — platform-fingerprints card); every WRITE uses
|
|
16
|
-
* the new name only, so a visitor's cart survives the rename.
|
|
17
|
-
*/
|
|
18
|
-
export async function addToCartAction(input: {
|
|
19
|
-
variantId: string
|
|
20
|
-
quantity: number
|
|
21
|
-
}): Promise<void> {
|
|
22
|
-
const client = await getServerClient()
|
|
23
|
-
const jar = await cookies()
|
|
24
|
-
const cartId = readCartCookie((name) => jar.get(name)?.value)
|
|
25
|
-
|
|
26
|
-
if (cartId) {
|
|
27
|
-
await addLineItem(client, cartId, {
|
|
28
|
-
variant_id: input.variantId,
|
|
29
|
-
quantity: input.quantity,
|
|
30
|
-
})
|
|
31
|
-
} else {
|
|
32
|
-
const { cart } = await createCart(client, {
|
|
33
|
-
currency_code: "eur",
|
|
34
|
-
items: [{ variant_id: input.variantId, quantity: input.quantity }],
|
|
35
|
-
})
|
|
36
|
-
jar.set(CART_COOKIE, cart.id, {
|
|
37
|
-
path: "/",
|
|
38
|
-
sameSite: "lax",
|
|
39
|
-
httpOnly: false, // the browser-side drawer persists the same cookie
|
|
40
|
-
maxAge: CART_COOKIE_MAX_AGE,
|
|
41
|
-
})
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Layout refetches the cart snapshot → CartDrawerProvider sees the new
|
|
45
|
-
// product-line count and auto-opens the drawer.
|
|
46
|
-
revalidatePath("/", "layout")
|
|
47
|
-
}
|