@cimplify/sdk 0.38.3 → 0.38.4

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.
@@ -117,6 +117,19 @@ interface MockBusiness {
117
117
  timezone?: string;
118
118
  metadata?: Record<string, unknown>;
119
119
  }
120
+ interface MockQuantityPricingTier {
121
+ min_quantity: number;
122
+ unit_price: string;
123
+ }
124
+ interface MockBillingPlan {
125
+ id: string;
126
+ name: string;
127
+ interval: "day" | "week" | "month" | "year";
128
+ interval_count: number;
129
+ price: string;
130
+ currency: string;
131
+ trial_period_days?: number;
132
+ }
120
133
  interface MockProduct {
121
134
  id: string;
122
135
  business_id: string;
@@ -140,6 +153,20 @@ interface MockProduct {
140
153
  pairings?: string[];
141
154
  is_signature?: boolean;
142
155
  is_new?: boolean;
156
+ /** Override for the auto-derived render hint. */
157
+ render_hint?: "food" | "physical" | "general";
158
+ /** Display mode hint (card opens modal, page is a link). Storefronts can override. */
159
+ display_mode?: "card" | "page";
160
+ /** For digital products — how the SDK renders fulfilment UI. */
161
+ digital_product_type?: "download" | "license" | "event_ticket" | "access_pass" | "gift_code";
162
+ /** For wholesale products — quantity-pricing tiers. */
163
+ quantity_pricing?: MockQuantityPricingTier[];
164
+ /** For services — duration unit (drives Rental/Accommodation/Lease cards). */
165
+ duration_unit?: "minutes" | "hours" | "days" | "nights" | "weeks" | "months" | "years";
166
+ /** For schedule services — duration in minutes (presence implies non-subscription service). */
167
+ duration_minutes?: number;
168
+ /** For subscription services — billing plans. */
169
+ billing_plans?: MockBillingPlan[];
143
170
  metadata?: Record<string, unknown>;
144
171
  created_at: string;
145
172
  updated_at: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cimplify/sdk",
3
- "version": "0.38.3",
3
+ "version": "0.38.4",
4
4
  "description": "Cimplify Commerce SDK for storefronts",
5
5
  "keywords": [
6
6
  "cimplify",
@@ -1,6 +1,6 @@
1
1
  # __STOREFRONT_NAME__
2
2
 
3
- A Cimplify storefront scaffolded from the SDK template — Next.js 15 (App Router), React 19, Tailwind v4, talking to the Cimplify mock API by default.
3
+ A Cimplify storefront scaffolded from the SDK template — Next.js 16 (App Router), React 19, Tailwind v4, talking to the Cimplify mock API by default.
4
4
 
5
5
  ## Run
6
6
 
@@ -36,7 +36,7 @@ export function CollectionStrip({ collection, products, collectionHref }: Collec
36
36
  <div className="grid grid-flow-col auto-cols-[minmax(220px,1fr)] gap-4 overflow-x-auto snap-x snap-mandatory pb-2">
37
37
  {products.slice(0, 8).map((p) => (
38
38
  <div key={p.id} className="snap-start">
39
- <StoreProductCard product={p} aspectRatio="square" />
39
+ <StoreProductCard product={p} />
40
40
  </div>
41
41
  ))}
42
42
  </div>
@@ -1,43 +1,94 @@
1
1
  "use client";
2
2
 
3
3
  import { useRouter, useSearchParams, usePathname } from "next/navigation";
4
- import { ProductCard } from "@cimplify/sdk/react";
5
- import type { ProductCardProps } from "@cimplify/sdk/react";
4
+ import {
5
+ CardVariant,
6
+ FoodProductCard,
7
+ RetailProductCard,
8
+ WholesaleProductCard,
9
+ DigitalProductCard,
10
+ BundleProductCard,
11
+ CompositeProductCard,
12
+ StandardServiceCard,
13
+ RentalServiceCard,
14
+ AccommodationCard,
15
+ LeaseServiceCard,
16
+ SubscriptionCard,
17
+ } from "@cimplify/sdk/react";
18
+ import type { CardLayoutProps } from "@cimplify/sdk/react";
6
19
  import type { Product } from "@cimplify/sdk";
20
+ import { PRODUCT_TYPE, RENDER_HINT, DURATION_UNIT } from "@cimplify/sdk";
7
21
 
8
- type Props = Omit<ProductCardProps, "displayMode" | "renderLink"> & {
9
- product: Product;
22
+ const RENTAL_UNITS = new Set<string>([DURATION_UNIT.Days, DURATION_UNIT.Hours]);
23
+ const LEASE_UNITS = new Set<string>([DURATION_UNIT.Weeks, DURATION_UNIT.Months, DURATION_UNIT.Years]);
24
+
25
+ const VARIANT_CARDS: Record<CardVariant, React.ComponentType<CardLayoutProps>> = {
26
+ [CardVariant.Food]: FoodProductCard,
27
+ [CardVariant.Retail]: RetailProductCard,
28
+ [CardVariant.Wholesale]: WholesaleProductCard,
29
+ [CardVariant.Digital]: DigitalProductCard,
30
+ [CardVariant.Bundle]: BundleProductCard,
31
+ [CardVariant.Composite]: CompositeProductCard,
32
+ [CardVariant.Standard]: StandardServiceCard,
33
+ [CardVariant.Rental]: RentalServiceCard,
34
+ [CardVariant.Accommodation]: AccommodationCard,
35
+ [CardVariant.Lease]: LeaseServiceCard,
36
+ [CardVariant.Subscription]: SubscriptionCard,
10
37
  };
11
38
 
39
+ function resolveVariant(p: Product): CardVariant {
40
+ if (p.type === PRODUCT_TYPE.Bundle) return CardVariant.Bundle;
41
+ if (p.type === PRODUCT_TYPE.Composite) return CardVariant.Composite;
42
+ if (p.quantity_pricing && p.quantity_pricing.length > 1) return CardVariant.Wholesale;
43
+ if (p.type === PRODUCT_TYPE.Digital) return CardVariant.Digital;
44
+ if (p.type === PRODUCT_TYPE.Service) {
45
+ if (p.duration_unit && RENTAL_UNITS.has(p.duration_unit)) return CardVariant.Rental;
46
+ if (p.duration_unit === DURATION_UNIT.Nights) return CardVariant.Accommodation;
47
+ if (p.duration_unit && LEASE_UNITS.has(p.duration_unit)) return CardVariant.Lease;
48
+ if (p.billing_plans && p.billing_plans.length > 0 && !p.duration_minutes) return CardVariant.Subscription;
49
+ return CardVariant.Standard;
50
+ }
51
+ if (p.render_hint === RENDER_HINT.Food) return CardVariant.Food;
52
+ return CardVariant.Retail;
53
+ }
54
+
55
+ interface Props {
56
+ product: Product;
57
+ /** Override the auto-detected card variant. */
58
+ variant?: CardVariant;
59
+ }
60
+
12
61
  /**
13
- * Wraps the SDK `<ProductCard>` so a click sets `?product=<slug>` instead of
14
- * opening the SDK's own modal. All variant card styling (Food, Bundle,
15
- * Composite, …) is preserved — we only intercept navigation so a single
16
- * deep-linkable `<ProductModal>` instance handles every product surface.
62
+ * Variant-aware product card that opens the URL-driven product modal on
63
+ * click (`?product=<slug>`). Renders the underlying SDK variant card
64
+ * (Food, Bundle, Composite, …) directly and supplies a `renderLink` that
65
+ * intercepts navigation — so a single `<CardShell>`-rendered `<a>` tag
66
+ * wraps the card (no nested anchors, no hydration error).
17
67
  */
18
- export function StoreProductCard(props: Props) {
68
+ export function StoreProductCard({ product, variant }: Props) {
19
69
  const router = useRouter();
20
70
  const pathname = usePathname();
21
71
  const searchParams = useSearchParams();
22
72
 
23
- const open = (slug: string) => {
73
+ const slug = product.slug || product.id;
74
+ const Card = VARIANT_CARDS[variant ?? resolveVariant(product)];
75
+
76
+ const open = () => {
24
77
  const next = new URLSearchParams(searchParams?.toString() ?? "");
25
78
  next.set("product", slug);
26
79
  router.push(`${pathname}?${next.toString()}`, { scroll: false });
27
80
  };
28
81
 
29
82
  return (
30
- <ProductCard
31
- {...props}
32
- displayMode="page"
33
- href={`?product=${props.product.slug || props.product.id}`}
83
+ <Card
84
+ product={product}
34
85
  renderLink={({ href, className, children }) => (
35
86
  <a
36
87
  href={href}
37
88
  className={className}
38
89
  onClick={(e) => {
39
90
  e.preventDefault();
40
- open(props.product.slug || props.product.id);
91
+ open();
41
92
  }}
42
93
  >
43
94
  {children}
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@cimplify/sdk": "^0.38.1",
15
- "next": "^15.1.0",
15
+ "next": "^16.2.4",
16
16
  "react": "^19.0.0",
17
17
  "react-dom": "^19.0.0"
18
18
  },
@@ -18,6 +18,6 @@
18
18
  "@/*": ["./*"]
19
19
  }
20
20
  },
21
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
21
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", ".next/dev/types/**/*.ts"],
22
22
  "exclude": ["node_modules"]
23
23
  }