create-cartbase 0.1.13 → 0.1.15
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 +4 -0
- package/dist/enclosing-project.js +82 -0
- package/dist/index.js +5 -1
- package/package.json +1 -1
- package/template/app/docs/BUILD-A-STOREFRONT.md +8 -4
- package/template/app/docs/collections.md +167 -167
- package/template/app/docs/components.md +1290 -1211
- package/template/app/docs/consent.md +18 -8
- package/template/app/docs/customers.md +6 -8
- package/template/app/docs/integrations.md +4 -2
- package/template/app/docs/products.md +316 -306
- package/template/app/package.json +1 -1
- package/template/app/src/app/layout.tsx +11 -7
- package/template/app/src/app/order/[id]/confirmed/page.tsx +4 -0
- package/template/app/src/app/page.tsx +4 -1
- package/template/app/src/app/products/[handle]/page.tsx +15 -2
- package/template/app/src/app/providers.tsx +37 -27
- package/template/app/src/app/search/page.tsx +4 -1
- package/template/app/src/lib/config.ts +11 -0
|
@@ -9,7 +9,7 @@ import { StorefrontTags } from "@cartbase/storefront/tracking/storefront-tags"
|
|
|
9
9
|
import { TrackInit } from "@cartbase/storefront/tracking/track-init"
|
|
10
10
|
import { CartButtonClient } from "@cartbase/storefront/common/cart-button-client"
|
|
11
11
|
import { createStorefrontMetadata, PlatformInit } from "@cartbase/storefront/platform"
|
|
12
|
-
import { BARTER_CLIENT_ID, readCartCookie } from "@/lib/config"
|
|
12
|
+
import { BARTER_CLIENT_ID, readCartCookie, STORE_LOCALE } from "@/lib/config"
|
|
13
13
|
import { getServerClient } from "@/lib/server-client"
|
|
14
14
|
import { Providers } from "./providers"
|
|
15
15
|
import "./globals.css"
|
|
@@ -126,14 +126,18 @@ export default async function RootLayout({
|
|
|
126
126
|
])
|
|
127
127
|
|
|
128
128
|
return (
|
|
129
|
-
<html lang=
|
|
129
|
+
<html lang={STORE_LOCALE.code}>
|
|
130
130
|
<body>
|
|
131
|
-
|
|
131
|
+
{/* The store's own consent switch decides the default: a store
|
|
132
|
+
that collects consent starts every visitor denied until they
|
|
133
|
+
choose, a store with the banner off starts them granted. The
|
|
134
|
+
prop is required, so a layout cannot leave it out. */}
|
|
135
|
+
<ConsentInit required={consentRes.consent.enabled} />
|
|
132
136
|
{/* Every marketing tag the store configured in the admin, mounted
|
|
133
|
-
from its own config: Meta, TikTok, Google (GA4 + Ads
|
|
134
|
-
tag), GTM. Nothing to wire per vendor — saving the ids
|
|
135
|
-
Settings → Integrations is the whole merchant-side act.
|
|
136
|
-
matters: ConsentInit sets the Consent Mode defaults
|
|
137
|
+
from its own config: Meta, TikTok, ChatGPT, Google (GA4 + Ads
|
|
138
|
+
on one tag), GTM. Nothing to wire per vendor — saving the ids
|
|
139
|
+
in Settings → Integrations is the whole merchant-side act.
|
|
140
|
+
Order matters: ConsentInit sets the Consent Mode defaults
|
|
137
141
|
synchronously ABOVE this, so every tag below inherits the
|
|
138
142
|
gate. */}
|
|
139
143
|
<StorefrontTags client={client} />
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
import { TrackOrderPurchase } from "@cartbase/storefront/tracking/track-order-purchase"
|
|
16
16
|
import { useTrackingConfig } from "@cartbase/storefront/tracking/use-tracking-config"
|
|
17
17
|
import { browserClient } from "@/lib/browser-client"
|
|
18
|
+
import { STORE_LOCALE } from "@/lib/config"
|
|
18
19
|
import { LAST_ORDER_STORAGE_KEY } from "../../../checkout/checkout-page-client"
|
|
19
20
|
|
|
20
21
|
/**
|
|
@@ -86,6 +87,9 @@ export default function OrderConfirmedPage() {
|
|
|
86
87
|
// the order.
|
|
87
88
|
paymentMethodName={"Cash on delivery"}
|
|
88
89
|
storeHref="/"
|
|
90
|
+
// The order pack, as a prop: this template reads its copy from props,
|
|
91
|
+
// not from the locale provider, and the prop is required.
|
|
92
|
+
labels={STORE_LOCALE.order}
|
|
89
93
|
/>
|
|
90
94
|
</>
|
|
91
95
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StoreTemplate } from "@cartbase/storefront/store/store-template"
|
|
2
2
|
import type { SortOptions } from "@cartbase/storefront/lib/sort-products"
|
|
3
|
-
import { PRICING_CONTEXT } from "@/lib/config"
|
|
3
|
+
import { PRICING_CONTEXT, STORE_LOCALE } from "@/lib/config"
|
|
4
4
|
import { getServerClient } from "@/lib/server-client"
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -20,6 +20,9 @@ export default async function HomePage({
|
|
|
20
20
|
sortBy={sortBy as SortOptions | undefined}
|
|
21
21
|
page={page}
|
|
22
22
|
pricingContext={PRICING_CONTEXT}
|
|
23
|
+
// Server-rendered: the locale provider cannot reach it, so the pack
|
|
24
|
+
// comes as a prop, and the prop is required.
|
|
25
|
+
labels={STORE_LOCALE.store}
|
|
23
26
|
/>
|
|
24
27
|
)
|
|
25
28
|
}
|
|
@@ -3,7 +3,7 @@ import { retrieveProduct } from "@cartbase/storefront/api/products"
|
|
|
3
3
|
import { StoreApiError } from "@cartbase/storefront/api/types"
|
|
4
4
|
import { ProductTemplate } from "@cartbase/storefront/products/product-template"
|
|
5
5
|
import { addToCartAction } from "@/lib/cart-actions"
|
|
6
|
-
import { PRICING_CONTEXT } from "@/lib/config"
|
|
6
|
+
import { PRICING_CONTEXT, STORE_LOCALE } from "@/lib/config"
|
|
7
7
|
import { getServerClient } from "@/lib/server-client"
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -13,10 +13,12 @@ import { getServerClient } from "@/lib/server-client"
|
|
|
13
13
|
*/
|
|
14
14
|
export default async function ProductPage({
|
|
15
15
|
params,
|
|
16
|
+
searchParams,
|
|
16
17
|
}: {
|
|
17
18
|
params: Promise<{ handle: string }>
|
|
19
|
+
searchParams: Promise<{ variant?: string }>
|
|
18
20
|
}) {
|
|
19
|
-
const { handle } = await params
|
|
21
|
+
const [{ handle }, { variant }] = await Promise.all([params, searchParams])
|
|
20
22
|
const client = await getServerClient()
|
|
21
23
|
|
|
22
24
|
let product
|
|
@@ -34,6 +36,12 @@ export default async function ProductPage({
|
|
|
34
36
|
product={product}
|
|
35
37
|
pricingContext={PRICING_CONTEXT}
|
|
36
38
|
addToCart={addToCartAction}
|
|
39
|
+
// The product page contract: the address names the variant
|
|
40
|
+
// (`?variant=<digits>`, Shopify's parameter), and the page hands it to
|
|
41
|
+
// the template so the FIRST paint is that variant's price, code and
|
|
42
|
+
// stock. Switching a variant rewrites the address in the browser
|
|
43
|
+
// without asking the server.
|
|
44
|
+
initialVariantId={variant}
|
|
37
45
|
// NO `promises` here, deliberately. Until 0.13.0 the library carried
|
|
38
46
|
// delivery, exchange and return promises as label DEFAULTS, so every
|
|
39
47
|
// store scaffolded from this file told shoppers "your package will
|
|
@@ -50,6 +58,11 @@ export default async function ProductPage({
|
|
|
50
58
|
// default for a store that has not decided yet. The physical-facts
|
|
51
59
|
// section appears on its own for products that HAVE facts, and is
|
|
52
60
|
// skipped for those that do not.
|
|
61
|
+
//
|
|
62
|
+
// The pack has to be handed over here, not just mounted at the root:
|
|
63
|
+
// the related-products strip is a server component and cannot read
|
|
64
|
+
// the locale provider, so its heading would stay English. Required.
|
|
65
|
+
labels={STORE_LOCALE.products}
|
|
53
66
|
/>
|
|
54
67
|
)
|
|
55
68
|
}
|
|
@@ -9,14 +9,20 @@ import {
|
|
|
9
9
|
shouldRenderBanner,
|
|
10
10
|
type ConsentSettings,
|
|
11
11
|
} from "@cartbase/storefront/tracking/consent"
|
|
12
|
-
import {
|
|
12
|
+
import { StorefrontLocaleProvider } from "@cartbase/storefront/locales"
|
|
13
|
+
import { CART_COOKIE, CART_COOKIE_MAX_AGE, STORE_LOCALE } from "@/lib/config"
|
|
13
14
|
import { browserClient } from "@/lib/browser-client"
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
|
-
* Client-side shell: ONE CartDrawerProvider per app
|
|
17
|
-
* mount rule) + the consent banner (consent.md: render
|
|
18
|
-
* only when `enabled && mode === "builtin"`; ConsentInit
|
|
19
|
-
* server layout as the first child of <body>).
|
|
17
|
+
* Client-side shell: the store's language, ONE CartDrawerProvider per app
|
|
18
|
+
* (cart-drawer family mount rule) + the consent banner (consent.md: render
|
|
19
|
+
* the builtin banner only when `enabled && mode === "builtin"`; ConsentInit
|
|
20
|
+
* stays in the server layout as the first child of <body>).
|
|
21
|
+
*
|
|
22
|
+
* The language is mounted once, here. Every client component inside reads
|
|
23
|
+
* it, the cart drawer included, so nothing is handed over below. The
|
|
24
|
+
* server-rendered templates cannot read it, so each page hands them their
|
|
25
|
+
* area (`labels={STORE_LOCALE.store}`), and those props are required.
|
|
20
26
|
*/
|
|
21
27
|
export function Providers({
|
|
22
28
|
cart,
|
|
@@ -27,28 +33,32 @@ export function Providers({
|
|
|
27
33
|
consent: ConsentSettings
|
|
28
34
|
children: React.ReactNode
|
|
29
35
|
}) {
|
|
30
|
-
|
|
36
|
+
// The store's own consent copy, in the store's language where it has it;
|
|
37
|
+
// pickConsentCopy falls back to English and then to whatever exists.
|
|
38
|
+
const copy = pickConsentCopy(consent.copy, STORE_LOCALE.code)
|
|
31
39
|
return (
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
40
|
+
<StorefrontLocaleProvider locale={STORE_LOCALE}>
|
|
41
|
+
<CartDrawerProvider
|
|
42
|
+
cart={cart}
|
|
43
|
+
client={browserClient}
|
|
44
|
+
onCartChange={(next) => {
|
|
45
|
+
// Persist the cart id (carts.md: the app owns the cart cookie).
|
|
46
|
+
document.cookie = `${CART_COOKIE}=${encodeURIComponent(
|
|
47
|
+
next.id
|
|
48
|
+
)};path=/;max-age=${CART_COOKIE_MAX_AGE};samesite=lax`
|
|
49
|
+
}}
|
|
50
|
+
>
|
|
51
|
+
{children}
|
|
52
|
+
<CartDrawerTemplate />
|
|
53
|
+
{shouldRenderBanner(consent) && copy && (
|
|
54
|
+
<ConsentBanner
|
|
55
|
+
copy={copy}
|
|
56
|
+
layout={consent.layout}
|
|
57
|
+
privacyHref={consent.privacy_href}
|
|
58
|
+
rejectOnFirstLayer={consent.reject_on_first_layer}
|
|
59
|
+
/>
|
|
60
|
+
)}
|
|
61
|
+
</CartDrawerProvider>
|
|
62
|
+
</StorefrontLocaleProvider>
|
|
53
63
|
)
|
|
54
64
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SearchTemplate } from "@cartbase/storefront/store/search-template"
|
|
2
|
-
import { PRICING_CONTEXT } from "@/lib/config"
|
|
2
|
+
import { PRICING_CONTEXT, STORE_LOCALE } from "@/lib/config"
|
|
3
3
|
import { getServerClient } from "@/lib/server-client"
|
|
4
4
|
|
|
5
5
|
/** Search page (runbook step 5) — fully URL-state driven search-template. */
|
|
@@ -15,6 +15,9 @@ export default async function SearchPage({
|
|
|
15
15
|
client={client}
|
|
16
16
|
searchParams={params}
|
|
17
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}
|
|
18
21
|
/>
|
|
19
22
|
)
|
|
20
23
|
}
|
|
@@ -26,5 +26,16 @@ export const CART_COOKIE_MAX_AGE = 60 * 60 * 24 * 30 // 30 days
|
|
|
26
26
|
/** Locale cookie read by the clients' `getLocale`. */
|
|
27
27
|
export const LOCALE_COOKIE = "_barter_locale"
|
|
28
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
|
+
|
|
29
40
|
/** Pricing context for every catalog surface (runbook step 5). EUR only. */
|
|
30
41
|
export const PRICING_CONTEXT = { currency_code: "eur" } as const
|