create-cartbase 0.1.0 → 0.1.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.
Files changed (39) hide show
  1. package/dist/index.js +3 -3
  2. package/package.json +1 -1
  3. package/template/app/docs/BUILD-A-STOREFRONT.md +6 -6
  4. package/template/app/docs/README.md +77 -76
  5. package/template/app/docs/auth.md +1 -1
  6. package/template/app/docs/carts.md +6 -6
  7. package/template/app/docs/categories.md +1 -1
  8. package/template/app/docs/checkout.md +127 -76
  9. package/template/app/docs/collections.md +1 -1
  10. package/template/app/docs/components.md +59 -60
  11. package/template/app/docs/consent.md +1 -1
  12. package/template/app/docs/content.md +1 -1
  13. package/template/app/docs/customers.md +1 -1
  14. package/template/app/docs/deploy.md +5 -5
  15. package/template/app/docs/gift-cards.md +1 -1
  16. package/template/app/docs/integrations.md +1 -1
  17. package/template/app/docs/menus.md +1 -1
  18. package/template/app/docs/metaobjects.md +1 -1
  19. package/template/app/docs/orders.md +1 -1
  20. package/template/app/docs/platform.md +126 -0
  21. package/template/app/docs/products.md +1 -1
  22. package/template/app/docs/redirects.md +1 -1
  23. package/template/app/docs/regions.md +5 -6
  24. package/template/app/docs/reviews.md +1 -1
  25. package/template/app/docs/search.md +1 -1
  26. package/template/app/docs/subscriptions.md +1 -1
  27. package/template/app/docs/variables.md +315 -0
  28. package/template/app/next-env.d.ts +6 -0
  29. package/template/app/next.config.ts +3 -3
  30. package/template/app/package.json +1 -1
  31. package/template/app/smoke.mjs +1 -1
  32. package/template/app/src/app/checkout/checkout-page-client.tsx +26 -10
  33. package/template/app/src/app/checkout/mypos-demo-tab.tsx +101 -0
  34. package/template/app/src/app/checkout/page.tsx +5 -6
  35. package/template/app/src/app/layout.tsx +113 -105
  36. package/template/app/src/app/order/[id]/confirmed/page.tsx +5 -4
  37. package/template/app/src/lib/cart-actions.ts +47 -43
  38. package/template/app/src/lib/config.ts +10 -5
  39. package/template/app/tsconfig.tsbuildinfo +1 -1
@@ -1,105 +1,113 @@
1
- import type { Metadata } from "next"
2
- import Link from "next/link"
3
- import { cookies } from "next/headers"
4
- import { retrieveCart, type Cart } from "@cartbase/storefront/api/carts"
5
- import { getConsent } from "@cartbase/storefront/api/consent"
6
- import { getMenu, type Menu } from "@cartbase/storefront/api/menus"
7
- import { ConsentInit } from "@cartbase/storefront/tracking/consent-init"
8
- import { CartButtonClient } from "@cartbase/storefront/common/cart-button-client"
9
- import { CART_COOKIE } from "@/lib/config"
10
- import { getServerClient } from "@/lib/server-client"
11
- import { Providers } from "./providers"
12
- import "./globals.css"
13
-
14
- export const metadata: Metadata = {
15
- title: "Barter Example Store",
16
- description: "Reference storefront built on @cartbase/storefront",
17
- }
18
-
19
- /**
20
- * Runbook steps 3–4: bootstrap store config at layout level, then mount
21
- * order inside <body>: ConsentInit FIRST (static, synchronous), then the
22
- * consent-gated UI, then navigation. A store with no menus 404s every
23
- * handle — render no nav, never crash (menus.md).
24
- */
25
- async function fetchMenu(handle: string): Promise<Menu | null> {
26
- const client = await getServerClient()
27
- try {
28
- const { menu } = await getMenu(client, handle)
29
- return menu
30
- } catch {
31
- return null // unknown/deleted handle (404) → graceful "no nav"
32
- }
33
- }
34
-
35
- function MenuNav({ menu }: { menu: Menu | null }) {
36
- if (!menu || menu.items.length === 0) return null
37
- return (
38
- <nav className="flex items-center gap-4">
39
- {menu.items.map((item) => (
40
- <Link
41
- key={`${item.title}-${item.url}`}
42
- href={item.url}
43
- className="text-sm text-muted-foreground hover:text-foreground"
44
- >
45
- {item.title}
46
- </Link>
47
- ))}
48
- </nav>
49
- )
50
- }
51
-
52
- export default async function RootLayout({
53
- children,
54
- }: {
55
- children: React.ReactNode
56
- }) {
57
- const client = await getServerClient()
58
- const jar = await cookies()
59
- const cartId = jar.get(CART_COOKIE)?.value ?? null
60
-
61
- const [consentRes, mainMenu, footerMenu, cart] = await Promise.all([
62
- getConsent(client),
63
- fetchMenu("main-menu"),
64
- fetchMenu("footer"),
65
- cartId
66
- ? retrieveCart(client, cartId)
67
- .then((res): Cart | null => res.cart)
68
- .catch(() => null) // stale cookie empty cart state
69
- : Promise.resolve(null),
70
- ])
71
-
72
- return (
73
- <html lang="en">
74
- <body>
75
- <ConsentInit />
76
- <Providers cart={cart} consent={consentRes.consent}>
77
- <header className="border-b border-border">
78
- <div className="max-w-7xl mx-auto px-4 h-16 flex items-center justify-between gap-6">
79
- <Link href="/" className="font-semibold text-lg">
80
- Barter Example Store
81
- </Link>
82
- <div className="flex items-center gap-6">
83
- <MenuNav menu={mainMenu} />
84
- <Link
85
- href="/search"
86
- className="text-sm text-muted-foreground hover:text-foreground"
87
- >
88
- Search
89
- </Link>
90
- <CartButtonClient cart={cart} />
91
- </div>
92
- </div>
93
- </header>
94
- <main>{children}</main>
95
- <footer className="border-t border-border mt-12">
96
- <div className="max-w-7xl mx-auto px-4 py-8 text-sm text-muted-foreground flex items-center justify-between">
97
- <span>Barter Example Store</span>
98
- <MenuNav menu={footerMenu} />
99
- </div>
100
- </footer>
101
- </Providers>
102
- </body>
103
- </html>
104
- )
105
- }
1
+ import type { Metadata } from "next"
2
+ import Link from "next/link"
3
+ import { cookies } from "next/headers"
4
+ import { retrieveCart, type Cart } from "@cartbase/storefront/api/carts"
5
+ import { getConsent } from "@cartbase/storefront/api/consent"
6
+ import { getMenu, type Menu } from "@cartbase/storefront/api/menus"
7
+ import { ConsentInit } from "@cartbase/storefront/tracking/consent-init"
8
+ import { CartButtonClient } from "@cartbase/storefront/common/cart-button-client"
9
+ import { createStorefrontMetadata, PlatformInit } from "@cartbase/storefront/platform"
10
+ import { BARTER_CLIENT_ID, readCartCookie } from "@/lib/config"
11
+ import { getServerClient } from "@/lib/server-client"
12
+ import { Providers } from "./providers"
13
+ import "./globals.css"
14
+
15
+ // createStorefrontMetadata (platform-fingerprints card) stamps
16
+ // `<meta name="generator" content="Cartbase" />` on every page — never
17
+ // hand-write `generator` here.
18
+ export const metadata: Metadata = createStorefrontMetadata({
19
+ title: "Barter Example Store",
20
+ description: "Reference storefront built on @cartbase/storefront",
21
+ })
22
+
23
+ /**
24
+ * Runbook steps 3–4: bootstrap store config at layout level, then mount
25
+ * order inside <body>: ConsentInit FIRST (static, synchronous), then the
26
+ * consent-gated UI, then navigation. A store with no menus 404s every
27
+ * handle — render no nav, never crash (menus.md).
28
+ */
29
+ async function fetchMenu(handle: string): Promise<Menu | null> {
30
+ const client = await getServerClient()
31
+ try {
32
+ const { menu } = await getMenu(client, handle)
33
+ return menu
34
+ } catch {
35
+ return null // unknown/deleted handle (404) graceful "no nav"
36
+ }
37
+ }
38
+
39
+ function MenuNav({ menu }: { menu: Menu | null }) {
40
+ if (!menu || menu.items.length === 0) return null
41
+ return (
42
+ <nav className="flex items-center gap-4">
43
+ {menu.items.map((item) => (
44
+ <Link
45
+ key={`${item.title}-${item.url}`}
46
+ href={item.url}
47
+ className="text-sm text-muted-foreground hover:text-foreground"
48
+ >
49
+ {item.title}
50
+ </Link>
51
+ ))}
52
+ </nav>
53
+ )
54
+ }
55
+
56
+ export default async function RootLayout({
57
+ children,
58
+ }: {
59
+ children: React.ReactNode
60
+ }) {
61
+ const client = await getServerClient()
62
+ const jar = await cookies()
63
+ // Back-compat: prefer the new `_cartbase_cart` cookie, fall back to the
64
+ // legacy `_barter_cart_id` name so an existing visitor's cart survives
65
+ // the rename (platform-fingerprints card).
66
+ const cartId = readCartCookie((name) => jar.get(name)?.value) ?? null
67
+
68
+ const [consentRes, mainMenu, footerMenu, cart] = await Promise.all([
69
+ getConsent(client),
70
+ fetchMenu("main-menu"),
71
+ fetchMenu("footer"),
72
+ cartId
73
+ ? retrieveCart(client, cartId)
74
+ .then((res): Cart | null => res.cart)
75
+ .catch(() => null) // stale cookie → empty cart state
76
+ : Promise.resolve(null),
77
+ ])
78
+
79
+ return (
80
+ <html lang="en">
81
+ <body>
82
+ <ConsentInit />
83
+ <PlatformInit storeId={BARTER_CLIENT_ID} />
84
+ <Providers cart={cart} consent={consentRes.consent}>
85
+ <header className="border-b border-border">
86
+ <div className="max-w-7xl mx-auto px-4 h-16 flex items-center justify-between gap-6">
87
+ <Link href="/" className="font-semibold text-lg">
88
+ Barter Example Store
89
+ </Link>
90
+ <div className="flex items-center gap-6">
91
+ <MenuNav menu={mainMenu} />
92
+ <Link
93
+ href="/search"
94
+ className="text-sm text-muted-foreground hover:text-foreground"
95
+ >
96
+ Search
97
+ </Link>
98
+ <CartButtonClient cart={cart} />
99
+ </div>
100
+ </div>
101
+ </header>
102
+ <main>{children}</main>
103
+ <footer className="border-t border-border mt-12">
104
+ <div className="max-w-7xl mx-auto px-4 py-8 text-sm text-muted-foreground flex items-center justify-between">
105
+ <span>Barter Example Store</span>
106
+ <MenuNav menu={footerMenu} />
107
+ </div>
108
+ </footer>
109
+ </Providers>
110
+ </body>
111
+ </html>
112
+ )
113
+ }
@@ -67,10 +67,11 @@ export default function OrderConfirmedPage() {
67
67
  order={orderForTemplate}
68
68
  totals={totals}
69
69
  items={cartItems.map(displayItemFromCartLine)}
70
- // This reference store enables exactly one provider (pp_manual); a
71
- // multi-provider store should stash the chosen provider id from its
72
- // checkout state alongside the order.
73
- paymentProviderId={"pp_manual"}
70
+ // This reference store checks out via one merchant method (pp_* kill:
71
+ // methods are merchant-named, provider-less); a multi-tender store
72
+ // should stash the chosen tender from its checkout state alongside
73
+ // the order.
74
+ paymentMethodName={"Cash on delivery"}
74
75
  storeHref="/"
75
76
  />
76
77
  )
@@ -1,43 +1,47 @@
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 } 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
- export async function addToCartAction(input: {
15
- variantId: string
16
- quantity: number
17
- }): Promise<void> {
18
- const client = await getServerClient()
19
- const jar = await cookies()
20
- const cartId = jar.get(CART_COOKIE)?.value
21
-
22
- if (cartId) {
23
- await addLineItem(client, cartId, {
24
- variant_id: input.variantId,
25
- quantity: input.quantity,
26
- })
27
- } else {
28
- const { cart } = await createCart(client, {
29
- currency_code: "eur",
30
- items: [{ variant_id: input.variantId, quantity: input.quantity }],
31
- })
32
- jar.set(CART_COOKIE, cart.id, {
33
- path: "/",
34
- sameSite: "lax",
35
- httpOnly: false, // the browser-side drawer persists the same cookie
36
- maxAge: CART_COOKIE_MAX_AGE,
37
- })
38
- }
39
-
40
- // Layout refetches the cart snapshot → CartDrawerProvider sees the new
41
- // product-line count and auto-opens the drawer.
42
- revalidatePath("/", "layout")
43
- }
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
+ }
@@ -1,12 +1,17 @@
1
1
  /** Shared storefront configuration (BUILD-A-STOREFRONT.md — inputs table). */
2
2
 
3
- export const BARTER_URL = process.env.NEXT_PUBLIC_BARTER_URL ?? ""
4
- export const BARTER_CLIENT_ID = process.env.NEXT_PUBLIC_BARTER_CLIENT_ID ?? ""
3
+ export const BARTER_URL = process.env.NEXT_PUBLIC_CARTBASE_URL ?? ""
4
+ export const BARTER_CLIENT_ID = process.env.NEXT_PUBLIC_CARTBASE_CLIENT_ID ?? ""
5
5
  export const BARTER_PUBLISHABLE_KEY =
6
- process.env.NEXT_PUBLIC_BARTER_PUBLISHABLE_KEY || undefined
6
+ process.env.NEXT_PUBLIC_CARTBASE_PUBLISHABLE_KEY || undefined
7
7
 
8
- /** Cart-id cookie — owned by the app (the SDK never persists the cart). */
9
- export const CART_COOKIE = "_barter_cart_id"
8
+ /**
9
+ * Cart-id cookie — owned by the app (the SDK never persists the cart);
10
+ * the NAME is owned by the library so every consumer emits the same wire
11
+ * fingerprint (platform-fingerprints card). `readCartCookie` is the
12
+ * backward-compat reader for the legacy `_barter_cart_id` name.
13
+ */
14
+ export { CART_COOKIE, readCartCookie } from "@cartbase/storefront/lib/cookie-names"
10
15
  export const CART_COOKIE_MAX_AGE = 60 * 60 * 24 * 30 // 30 days
11
16
 
12
17
  /** Locale cookie read by the clients' `getLocale`. */