@kasuvia/sdk 0.0.8 → 0.0.9

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 (176) hide show
  1. package/README.md +42 -15
  2. package/dist/AddressAutocomplete-CjcqPElD.d.mts +46 -0
  3. package/dist/AddressAutocomplete-CvJHuRkf.d.ts +46 -0
  4. package/dist/components/index.d.mts +497 -0
  5. package/dist/components/index.d.ts +497 -0
  6. package/dist/components/index.js +4217 -0
  7. package/dist/components/index.js.map +1 -0
  8. package/dist/components/index.mjs +4194 -0
  9. package/dist/components/index.mjs.map +1 -0
  10. package/dist/config/server.d.mts +26 -0
  11. package/dist/config/server.d.ts +26 -0
  12. package/dist/config/server.js +38 -0
  13. package/dist/config/server.js.map +1 -0
  14. package/dist/config/server.mjs +13 -0
  15. package/dist/config/server.mjs.map +1 -0
  16. package/dist/hooks/index.d.mts +393 -160
  17. package/dist/hooks/index.d.ts +393 -160
  18. package/dist/hooks/index.js +2792 -2137
  19. package/dist/hooks/index.js.map +1 -1
  20. package/dist/hooks/index.mjs +2734 -2121
  21. package/dist/hooks/index.mjs.map +1 -1
  22. package/dist/index-CNW_LINy.d.mts +578 -0
  23. package/dist/index-CNW_LINy.d.ts +578 -0
  24. package/dist/{index-C1chUA3P.d.mts → index-F7N6UItS.d.mts} +1 -1
  25. package/dist/{index-C1chUA3P.d.ts → index-F7N6UItS.d.ts} +1 -1
  26. package/dist/index-NkI10J_E.d.mts +40 -0
  27. package/dist/index-NkI10J_E.d.ts +40 -0
  28. package/dist/index.d.mts +20437 -103
  29. package/dist/index.d.ts +20437 -103
  30. package/dist/index.js +2111 -1802
  31. package/dist/index.js.map +1 -1
  32. package/dist/index.mjs +2006 -1786
  33. package/dist/index.mjs.map +1 -1
  34. package/dist/media.service-Cx7dQjzX.d.mts +1393 -0
  35. package/dist/media.service-mrBFJ2UQ.d.ts +1393 -0
  36. package/dist/services/index.d.mts +908 -2
  37. package/dist/services/index.d.ts +908 -2
  38. package/dist/services/index.js +1492 -1686
  39. package/dist/services/index.js.map +1 -1
  40. package/dist/services/index.mjs +1431 -1670
  41. package/dist/services/index.mjs.map +1 -1
  42. package/package.json +46 -6
  43. package/src/components/bespoke/BespokeAvailabilityBadge.tsx +32 -0
  44. package/src/components/bespoke/BespokeProgressTracker.tsx +58 -0
  45. package/src/components/bespoke/index.ts +3 -0
  46. package/src/components/booking/SlotPicker.tsx +97 -0
  47. package/src/components/booking/index.ts +1 -0
  48. package/src/components/cart/CartItem.tsx +81 -0
  49. package/src/components/cart/CartSidebar.tsx +115 -0
  50. package/src/components/cart/CartTypeMismatchBanner.tsx +55 -0
  51. package/src/components/cart/index.ts +3 -0
  52. package/src/components/index.ts +8 -0
  53. package/src/components/orders/DeliverableReviewer.tsx +139 -0
  54. package/src/components/orders/index.ts +1 -0
  55. package/src/components/product/ProductCard.tsx +350 -0
  56. package/src/components/product/ProductGrid.tsx +84 -0
  57. package/src/components/product/index.ts +2 -0
  58. package/src/components/shared/AddressAutocomplete.tsx +212 -0
  59. package/src/components/shared/AnalyticsScripts.tsx +56 -0
  60. package/src/components/shared/CookieConsentBanner.tsx +98 -0
  61. package/src/components/shared/ImageEditorModal.tsx +1066 -0
  62. package/src/components/shared/ImageViewerModal.tsx +598 -0
  63. package/src/components/shared/LucideReactIcon.tsx +52 -0
  64. package/src/components/shared/MapPinDrop.tsx +101 -0
  65. package/src/components/shared/MultiSelectPicker.tsx +193 -0
  66. package/src/components/shared/OfflineBanner.tsx +46 -0
  67. package/src/components/shared/SearchableInput.tsx +153 -0
  68. package/src/components/shared/SearchablePopover.tsx +166 -0
  69. package/src/components/shared/SmartImage.tsx +311 -0
  70. package/src/components/shared/TruncatedValue.tsx +27 -0
  71. package/src/components/shared/UnsavedChangesGuard.tsx +138 -0
  72. package/src/components/shared/index.ts +15 -0
  73. package/src/components/skeletons/CardSkeleton.tsx +26 -0
  74. package/src/components/skeletons/CartItemSkeleton.tsx +26 -0
  75. package/src/components/skeletons/OrderItemSkeleton.tsx +26 -0
  76. package/src/components/skeletons/ProductCardSkeleton.tsx +26 -0
  77. package/src/components/skeletons/TableSkeleton.tsx +42 -0
  78. package/src/components/skeletons/index.ts +5 -0
  79. package/src/config/index.ts +22 -4
  80. package/src/config/server.ts +45 -0
  81. package/src/context/sdk-context.tsx +5 -2
  82. package/src/generated/auth-openapi.ts +2935 -0
  83. package/src/generated/cart-openapi.ts +652 -0
  84. package/src/generated/cms-openapi.ts +1668 -0
  85. package/src/generated/crm-openapi.ts +986 -0
  86. package/src/generated/graphql.ts +126 -0
  87. package/src/generated/index.ts +11 -0
  88. package/src/generated/media-openapi.ts +613 -0
  89. package/src/generated/notification-openapi.ts +862 -0
  90. package/src/generated/payment-openapi.ts +2713 -0
  91. package/src/generated/store-openapi.ts +9510 -0
  92. package/src/graphql/operations/collections.graphql +32 -0
  93. package/src/graphql/operations/products.graphql +197 -0
  94. package/src/graphql/operations/wishlist.graphql +25 -0
  95. package/src/hooks/index.ts +9 -2
  96. package/src/hooks/use-auth.ts +163 -69
  97. package/src/hooks/use-bespoke-availability.ts +39 -22
  98. package/src/hooks/use-business-config.ts +51 -0
  99. package/src/hooks/use-cart.ts +310 -238
  100. package/src/hooks/use-cms.ts +107 -127
  101. package/src/hooks/use-collections.ts +35 -55
  102. package/src/hooks/use-consultations.ts +9 -136
  103. package/src/hooks/use-countries.ts +19 -28
  104. package/src/hooks/use-currencies.ts +8 -20
  105. package/src/hooks/use-customer-address.ts +64 -0
  106. package/src/hooks/use-feedback.ts +34 -0
  107. package/src/hooks/use-languages.ts +26 -39
  108. package/src/hooks/use-media-upload.ts +23 -0
  109. package/src/hooks/use-notification-stream.ts +88 -0
  110. package/src/hooks/use-notifications.ts +163 -0
  111. package/src/hooks/use-order-stream.ts +29 -29
  112. package/src/hooks/use-orders.ts +91 -73
  113. package/src/hooks/use-payment.ts +116 -84
  114. package/src/hooks/use-products.ts +65 -269
  115. package/src/hooks/use-push-notifications.ts +122 -0
  116. package/src/hooks/use-reviews.ts +175 -0
  117. package/src/hooks/use-service-booking.ts +109 -132
  118. package/src/hooks/use-states.ts +16 -30
  119. package/src/hooks/use-wishlist.ts +44 -69
  120. package/src/index.ts +15 -11
  121. package/src/services/auth.service.ts +422 -132
  122. package/src/services/bespoke.service.ts +69 -10
  123. package/src/services/cart.service.ts +155 -186
  124. package/src/services/cms.service.ts +41 -50
  125. package/src/services/collection.service.ts +25 -63
  126. package/src/services/config.service.ts +10 -21
  127. package/src/services/consultation.service.ts +36 -93
  128. package/src/services/feedback.service.ts +76 -0
  129. package/src/services/index.ts +7 -1
  130. package/src/services/legal.service.ts +62 -0
  131. package/src/services/logistics.service.ts +235 -53
  132. package/src/services/media.service.ts +115 -0
  133. package/src/services/notifications.service.ts +204 -20
  134. package/src/services/order.service.ts +498 -127
  135. package/src/services/payment.service.ts +149 -100
  136. package/src/services/product.service.ts +157 -814
  137. package/src/services/review.service.ts +211 -0
  138. package/src/services/service-booking.service.ts +198 -99
  139. package/src/services/wishlist.service.ts +27 -74
  140. package/src/types/auth/index.ts +161 -18
  141. package/src/types/business/index.ts +9 -1
  142. package/src/types/cms/index.ts +91 -6
  143. package/src/types/cms/legal.ts +18 -0
  144. package/src/types/consultations/index.ts +60 -68
  145. package/src/types/index.ts +5 -3
  146. package/src/types/media/index.ts +35 -3
  147. package/src/types/payments/index.ts +80 -16
  148. package/src/types/reviews/index.ts +116 -0
  149. package/src/types/services/index.ts +113 -38
  150. package/src/types/store/checkout.ts +53 -0
  151. package/src/types/store/index.ts +273 -52
  152. package/src/types/store/invoice.ts +31 -0
  153. package/src/types/store/shipping.ts +84 -3
  154. package/src/types/ui/index.ts +1 -1
  155. package/src/utils/checkout.ts +19 -3
  156. package/src/utils/cookieConsent.ts +34 -0
  157. package/src/utils/format.ts +408 -0
  158. package/src/utils/geolocation.ts +134 -0
  159. package/src/utils/graphql-url.ts +47 -0
  160. package/src/utils/guest-session.ts +59 -0
  161. package/src/utils/http-client.ts +147 -0
  162. package/src/utils/index.ts +11 -4
  163. package/src/utils/status-colors.ts +79 -0
  164. package/src/utils/token-refresh.ts +115 -0
  165. package/src/utils/validators.ts +13 -0
  166. package/src/utils/youtube.ts +18 -0
  167. package/dist/index-C5YO9gTl.d.mts +0 -507
  168. package/dist/index-C5YO9gTl.d.ts +0 -507
  169. package/dist/index-CefG7_xq.d.mts +0 -793
  170. package/dist/index-DeHCf3zI.d.ts +0 -793
  171. package/src/graphql/collections.graphql.ts +0 -48
  172. package/src/graphql/index.ts +0 -7
  173. package/src/graphql/products.graphql.ts +0 -860
  174. package/src/graphql/wishlist.graphql.ts +0 -34
  175. package/src/hooks/use-consultation-stream.ts +0 -142
  176. package/src/types/bespoke/index.ts +0 -39
package/README.md CHANGED
@@ -10,49 +10,76 @@ pnpm install @kasuvia/sdk
10
10
 
11
11
  ## Usage
12
12
 
13
- ### Initialize SDK
13
+ The SDK is stateless — there is no `initSdk()` call. Every service function takes a
14
+ `SdkConfig` object as its first argument, and every hook accepts an optional `SdkConfig`
15
+ argument (falling back to React Context if you wrap your tree in `<SdkProvider>`).
16
+
17
+ ### Configuration
14
18
 
15
19
  ```ts
16
- import { initSdk, configureHttpClients } from '@kasuvia/sdk'
20
+ import type { SdkConfig } from '@kasuvia/sdk'
17
21
 
18
- initSdk({
22
+ const config: SdkConfig = {
19
23
  businessId: 'B-123ABC',
20
24
  storefrontApiKey: 'your-api-key',
21
- environment: 'production' // or 'development' for local development
22
- })
23
-
24
- // Configure HTTP clients after initialization
25
- configureHttpClients()
25
+ backendUrls: {
26
+ auth: 'https://api.kasuvia.com/auth',
27
+ store: 'https://api.kasuvia.com/store',
28
+ payment: 'https://api.kasuvia.com/payment',
29
+ notifications: 'https://api.kasuvia.com/notifications',
30
+ cms: 'https://api.kasuvia.com/cms',
31
+ },
32
+ }
26
33
  ```
27
34
 
35
+ `backendUrls` should only be resolved on the server (or in a Next.js API route) — never
36
+ shipped to the browser directly.
37
+
28
38
  ### Use React Hooks
29
39
 
40
+ Wrap your tree once so hooks don't need explicit config on every call:
41
+
42
+ ```tsx
43
+ import { SdkProvider } from '@kasuvia/sdk/hooks'
44
+
45
+ function RootLayout({ children }) {
46
+ return (
47
+ <SdkProvider config={config} accessToken={accessToken}>
48
+ {children}
49
+ </SdkProvider>
50
+ )
51
+ }
52
+ ```
53
+
30
54
  ```ts
31
- import { useCart, useOrders, useAuth } from '@kasuvia/sdk'
55
+ import { useCart, useOrders, useLogin } from '@kasuvia/sdk/hooks'
32
56
 
33
57
  function Cart() {
34
- const { cart, addToCart, isLoading } = useCart()
58
+ const { cart, items, count, isLoading, addItem } = useCart() // custom return shape, not raw React Query `data`
35
59
  // ...
36
60
  }
37
61
 
38
62
  function Orders() {
39
- const { orders } = useOrders()
63
+ const { data: orders } = useOrders()
40
64
  // ...
41
65
  }
42
66
 
43
67
  function Auth() {
44
- const { login } = useLogin()
68
+ const { mutate: login } = useLogin() // standard React Query useMutation result
45
69
  // ...
46
70
  }
47
71
  ```
48
72
 
73
+ Hooks also accept an explicit `config`/`accessToken` if you're not using `<SdkProvider>`
74
+ — check each hook's signature in `src/hooks/`, as parameter order varies per hook.
75
+
49
76
  ### Use Services Directly
50
77
 
51
78
  ```ts
52
- import { getCart, addToCart } from '@kasuvia/sdk'
79
+ import { getCart, addToCart } from '@kasuvia/sdk/services'
53
80
 
54
- const cart = await getCart()
55
- const updated = await addToCart({ product_id: '123', quantity: 1 })
81
+ const cart = await getCart(config, accessToken)
82
+ const updated = await addToCart(config, accessToken, { product_id: '123', quantity: 1, /* ...see full item shape in src/services/cart.service.ts */ })
56
83
  ```
57
84
 
58
85
  ## Features
@@ -0,0 +1,46 @@
1
+ import * as React from 'react';
2
+ import { S as SdkConfig } from './index-NkI10J_E.mjs';
3
+
4
+ interface AddressSuggestion {
5
+ place_id?: string | number;
6
+ display_name: string;
7
+ line1: string;
8
+ city?: string;
9
+ state?: string;
10
+ postal_code?: string;
11
+ country?: string;
12
+ latitude?: number;
13
+ longitude?: number;
14
+ }
15
+ interface AddressAutocompleteProps {
16
+ /** The street-address line 1 text. */
17
+ value: string;
18
+ /** Fires on every keystroke - the field stays fully editable. */
19
+ onChange: (line1: string) => void;
20
+ /** Fires once when a suggestion is picked, with structured components. */
21
+ onResolve?: (suggestion: AddressSuggestion) => void;
22
+ /** ISO-2 country code to bias results toward (e.g. "NG"). */
23
+ country?: string;
24
+ disabled?: boolean;
25
+ placeholder?: string;
26
+ className?: string;
27
+ id?: string;
28
+ "aria-label"?: string;
29
+ config?: SdkConfig;
30
+ }
31
+ /**
32
+ * REAL BUG FIXED (2026-09-22): this called a hardcoded third-party endpoint
33
+ * (`nominatim.openstreetmap.org`) directly from the browser — a completely
34
+ * different data source than the rest of this platform's address/logistics
35
+ * system, which already has a real, working backend endpoint for exactly
36
+ * this (`GET /logistics/address/autocomplete`, `autocompleteAddress` in
37
+ * logistics.service.ts, live-verified this session). Calling Nominatim
38
+ * directly is also a real operational risk: OpenStreetMap's usage policy
39
+ * rate-limits and blocks unauthenticated browser traffic at production
40
+ * volume, and it has no idea this business is in Nigeria the way the real
41
+ * backend's geocoding provider is tuned to be. Rebuilt to call the real
42
+ * backend through the SDK's own service function instead.
43
+ */
44
+ declare function AddressAutocomplete({ value, onChange, onResolve, country, disabled, placeholder, className, id, config, ...rest }: AddressAutocompleteProps): React.JSX.Element;
45
+
46
+ export { AddressAutocomplete as A, type AddressAutocompleteProps as a, type AddressSuggestion as b };
@@ -0,0 +1,46 @@
1
+ import * as React from 'react';
2
+ import { S as SdkConfig } from './index-NkI10J_E.js';
3
+
4
+ interface AddressSuggestion {
5
+ place_id?: string | number;
6
+ display_name: string;
7
+ line1: string;
8
+ city?: string;
9
+ state?: string;
10
+ postal_code?: string;
11
+ country?: string;
12
+ latitude?: number;
13
+ longitude?: number;
14
+ }
15
+ interface AddressAutocompleteProps {
16
+ /** The street-address line 1 text. */
17
+ value: string;
18
+ /** Fires on every keystroke - the field stays fully editable. */
19
+ onChange: (line1: string) => void;
20
+ /** Fires once when a suggestion is picked, with structured components. */
21
+ onResolve?: (suggestion: AddressSuggestion) => void;
22
+ /** ISO-2 country code to bias results toward (e.g. "NG"). */
23
+ country?: string;
24
+ disabled?: boolean;
25
+ placeholder?: string;
26
+ className?: string;
27
+ id?: string;
28
+ "aria-label"?: string;
29
+ config?: SdkConfig;
30
+ }
31
+ /**
32
+ * REAL BUG FIXED (2026-09-22): this called a hardcoded third-party endpoint
33
+ * (`nominatim.openstreetmap.org`) directly from the browser — a completely
34
+ * different data source than the rest of this platform's address/logistics
35
+ * system, which already has a real, working backend endpoint for exactly
36
+ * this (`GET /logistics/address/autocomplete`, `autocompleteAddress` in
37
+ * logistics.service.ts, live-verified this session). Calling Nominatim
38
+ * directly is also a real operational risk: OpenStreetMap's usage policy
39
+ * rate-limits and blocks unauthenticated browser traffic at production
40
+ * volume, and it has no idea this business is in Nigeria the way the real
41
+ * backend's geocoding provider is tuned to be. Rebuilt to call the real
42
+ * backend through the SDK's own service function instead.
43
+ */
44
+ declare function AddressAutocomplete({ value, onChange, onResolve, country, disabled, placeholder, className, id, config, ...rest }: AddressAutocompleteProps): React.JSX.Element;
45
+
46
+ export { AddressAutocomplete as A, type AddressAutocompleteProps as a, type AddressSuggestion as b };
@@ -0,0 +1,497 @@
1
+ import * as React from 'react';
2
+ import React__default, { ReactNode, CSSProperties } from 'react';
3
+ import { a as CartItem$1, P as Product, L as ListParams, O as Order } from '../index-CNW_LINy.mjs';
4
+ export { A as AddressAutocomplete, a as AddressAutocompleteProps, b as AddressSuggestion } from '../AddressAutocomplete-CjcqPElD.mjs';
5
+ import { LucideProps } from 'lucide-react';
6
+ import { HTMLMotionProps } from 'framer-motion';
7
+ import '../index-NkI10J_E.mjs';
8
+
9
+ interface TableSkeletonProps {
10
+ rows?: number;
11
+ columns?: number;
12
+ className?: string;
13
+ rowClassName?: string;
14
+ }
15
+ declare function TableSkeleton({ rows, columns, className, rowClassName }: TableSkeletonProps): React__default.JSX.Element;
16
+
17
+ interface CardSkeletonProps {
18
+ count?: number;
19
+ containerClassName?: string;
20
+ cardClassName?: string;
21
+ }
22
+ declare function CardSkeleton({ count, containerClassName, cardClassName }: CardSkeletonProps): React__default.JSX.Element;
23
+
24
+ interface CartItemSkeletonProps {
25
+ count?: number;
26
+ className?: string;
27
+ }
28
+ declare function CartItemSkeleton({ count, className }: CartItemSkeletonProps): React__default.JSX.Element;
29
+
30
+ interface OrderItemSkeletonProps {
31
+ count?: number;
32
+ className?: string;
33
+ }
34
+ declare function OrderItemSkeleton({ count, className }: OrderItemSkeletonProps): React__default.JSX.Element;
35
+
36
+ interface ProductCardSkeletonProps {
37
+ className?: string;
38
+ imageClassName?: string;
39
+ }
40
+ declare function ProductCardSkeleton({ className, imageClassName, }: ProductCardSkeletonProps): React__default.JSX.Element;
41
+
42
+ interface CartItemProps {
43
+ item: CartItem$1;
44
+ onUpdateQty: (itemId: string, qty: number) => void;
45
+ onRemove: (itemId: string) => void;
46
+ formatPriceFn?: (price: number) => string;
47
+ className?: string;
48
+ imageClassName?: string;
49
+ titleClassName?: string;
50
+ priceClassName?: string;
51
+ quantityGroupClassName?: string;
52
+ buttonClassName?: string;
53
+ removeButtonClassName?: string;
54
+ }
55
+ declare function CartItem({ item, onUpdateQty, onRemove, formatPriceFn, className, imageClassName, titleClassName, priceClassName, quantityGroupClassName, buttonClassName, removeButtonClassName, }: CartItemProps): React__default.JSX.Element;
56
+
57
+ interface CartSidebarProps {
58
+ onCheckout?: () => void;
59
+ onClose?: () => void;
60
+ formatPriceFn?: (price: number) => string;
61
+ className?: string;
62
+ headerClassName?: string;
63
+ titleClassName?: string;
64
+ bodyClassName?: string;
65
+ footerClassName?: string;
66
+ checkoutButtonClassName?: string;
67
+ }
68
+ declare function CartSidebar({ onCheckout, onClose, formatPriceFn, className, headerClassName, titleClassName, bodyClassName, footerClassName, checkoutButtonClassName, }: CartSidebarProps): React__default.JSX.Element;
69
+
70
+ interface CartTypeMismatchBannerProps {
71
+ mismatch: {
72
+ message: string;
73
+ existing_type: string;
74
+ new_type: string;
75
+ };
76
+ onClearAndAdd: () => void;
77
+ onKeepCurrent: () => void;
78
+ className?: string;
79
+ titleClassName?: string;
80
+ messageClassName?: string;
81
+ buttonGroupClassName?: string;
82
+ confirmButtonClassName?: string;
83
+ cancelButtonClassName?: string;
84
+ }
85
+ declare function CartTypeMismatchBanner({ mismatch, onClearAndAdd, onKeepCurrent, className, titleClassName, messageClassName, buttonGroupClassName, confirmButtonClassName, cancelButtonClassName, }: CartTypeMismatchBannerProps): React__default.JSX.Element;
86
+
87
+ interface ProductCardProps {
88
+ product: Product;
89
+ onSelectProduct?: (product: Product) => void;
90
+ onWishlistToggle?: (product: Product, e: React__default.MouseEvent) => void;
91
+ isWishlistSaved?: boolean;
92
+ showWishlistButton?: boolean;
93
+ priority?: boolean;
94
+ className?: string;
95
+ /** Sizing/aspect-ratio classes for the image's container (e.g. "aspect-square").
96
+ * Kept separate from `imageClassName` because SmartImage renders the image
97
+ * in `fill` mode — sizing has to live on the container, not the <img>. */
98
+ imageContainerClassName?: string;
99
+ imageClassName?: string;
100
+ titleClassName?: string;
101
+ priceClassName?: string;
102
+ buttonClassName?: string;
103
+ enableImageCarousel?: boolean;
104
+ showCarouselDots?: boolean;
105
+ showCarouselArrows?: boolean;
106
+ activeDotClassName?: string;
107
+ dotClassName?: string;
108
+ formatPriceFn?: (price: number) => string;
109
+ /** Rendered top-left over the image. Falls back to a plain product-type
110
+ * badge when omitted — pass your own (e.g. New/Popular/On Sale badges) to
111
+ * replace it entirely. */
112
+ badgeSlot?: React__default.ReactNode;
113
+ /**
114
+ * Built-in star-rating row (product.average_rating / rating_count — real
115
+ * platform fields, synced from kasuvia-crm's review pipeline). On by
116
+ * default when a product actually has a rating; a storefront with no use
117
+ * for it, or that wants to build its own, can pass `showRating={false}`.
118
+ */
119
+ showRating?: boolean;
120
+ ratingClassName?: string;
121
+ /**
122
+ * Generic composable slot rendered between the price row and the CTA
123
+ * button — for "and many more" (stock urgency, shipping badges, a custom
124
+ * promo line, anything else a specific storefront wants) without forking
125
+ * this component. Renders nothing by default.
126
+ */
127
+ footerSlot?: React__default.ReactNode;
128
+ }
129
+ declare function ProductCard({ product, onSelectProduct, onWishlistToggle, isWishlistSaved, showWishlistButton, priority, className, imageContainerClassName, imageClassName, titleClassName, priceClassName, buttonClassName, enableImageCarousel, showCarouselDots, showCarouselArrows, activeDotClassName, dotClassName, formatPriceFn, badgeSlot, showRating, ratingClassName, footerSlot, }: ProductCardProps): React__default.JSX.Element;
130
+
131
+ interface ProductGridProps {
132
+ params?: ListParams;
133
+ onSelectProduct?: (product: Product) => void;
134
+ onWishlistToggle?: (product: Product, e: React__default.MouseEvent) => void;
135
+ isWishlistSavedFn?: (product: Product) => boolean;
136
+ showWishlistButton?: boolean;
137
+ formatPriceFn?: (price: number) => string;
138
+ className?: string;
139
+ cardClassName?: string;
140
+ }
141
+ declare function ProductGrid({ params, onSelectProduct, onWishlistToggle, isWishlistSavedFn, showWishlistButton, formatPriceFn, className, cardClassName, }: ProductGridProps): React__default.JSX.Element;
142
+
143
+ interface SlotPickerProps {
144
+ productId: string;
145
+ /** A single date (YYYY-MM-DD) — the real backend checks one day at a time. */
146
+ date: string;
147
+ staffId?: string;
148
+ locationId?: string;
149
+ onSelectSlot: (time: string) => void;
150
+ className?: string;
151
+ slotButtonClassName?: string;
152
+ }
153
+ /**
154
+ * REAL BUG FIXED (2026-09-20): rebuilt against the real backend response
155
+ * (GET /catalog/products/{id}/available-slots — a single `date`, not a
156
+ * dateFrom/dateTo range; `available_slots` is a flat array of ISO time
157
+ * strings, not slot objects with start_time/end_time/slots_remaining, none
158
+ * of which the real endpoint returns).
159
+ */
160
+ declare function SlotPicker({ productId, date, staffId, locationId, onSelectSlot, className, slotButtonClassName, }: SlotPickerProps): React__default.JSX.Element;
161
+
162
+ interface BespokeAvailabilityBadgeProps {
163
+ productId: string;
164
+ className?: string;
165
+ }
166
+ declare function BespokeAvailabilityBadge({ productId, className }: BespokeAvailabilityBadgeProps): React__default.JSX.Element | null;
167
+
168
+ type BespokeStageKey = 'received' | 'development' | 'finished' | 'dispatched';
169
+ interface BespokeProgressTrackerProps {
170
+ stage: BespokeStageKey;
171
+ className?: string;
172
+ activeStepClassName?: string;
173
+ completedStepClassName?: string;
174
+ inactiveStepClassName?: string;
175
+ }
176
+ declare function BespokeProgressTracker({ stage, className, activeStepClassName, completedStepClassName, inactiveStepClassName, }: BespokeProgressTrackerProps): React__default.JSX.Element;
177
+
178
+ interface DeliverableReviewerProps {
179
+ order: Order;
180
+ className?: string;
181
+ }
182
+ /**
183
+ * REAL BUG FIXED (2026-09-20): rebuilt entirely — this used to fetch a
184
+ * "/service/projects/{order_id}/deliverables/" list that doesn't exist
185
+ * anywhere in the backend. The real bespoke deliverable model is a SINGLE
186
+ * proofing record per order (BespokeFulfillmentResponse, embedded as
187
+ * `order.bespoke_fulfillment` and flattened onto the SDK's Order type as
188
+ * `bespoke_*` fields by normaliseStorefrontOrder), not a fetchable list —
189
+ * there is no separate GET endpoint for it at all. Approve/request-revision
190
+ * now call the real endpoints (POST /orders/{id}/bespoke/approve,
191
+ * .../request-revision) instead of a fictional deliverable-id-scoped route.
192
+ */
193
+ declare function DeliverableReviewer({ order, className }: DeliverableReviewerProps): React__default.JSX.Element;
194
+
195
+ interface TruncatedValueProps {
196
+ /** The (possibly visually truncated, or compact/abbreviated) displayed value. */
197
+ value: ReactNode;
198
+ /** The full, untruncated/unabbreviated value shown on hover/title. */
199
+ fullValue?: ReactNode;
200
+ className?: string;
201
+ triggerClassName?: string;
202
+ }
203
+ /**
204
+ * TruncatedValue
205
+ * Wraps a string or numeric value so the full value is accessible on hover or title.
206
+ */
207
+ declare function TruncatedValue({ value, fullValue, className, triggerClassName }: TruncatedValueProps): React.JSX.Element;
208
+
209
+ interface OfflineBannerProps {
210
+ message?: string;
211
+ className?: string;
212
+ }
213
+ declare function OfflineBanner({ message, className, }: OfflineBannerProps): React.JSX.Element | null;
214
+
215
+ interface MapPinDropProps {
216
+ initialLatitude?: number;
217
+ initialLongitude?: number;
218
+ onPinDrop?: (coordinates: {
219
+ latitude: number;
220
+ longitude: number;
221
+ }) => void;
222
+ value?: {
223
+ latitude: number;
224
+ longitude: number;
225
+ } | null;
226
+ height?: string;
227
+ disabled?: boolean;
228
+ zoom?: number;
229
+ }
230
+ declare function MapPinDrop({ initialLatitude, initialLongitude, onPinDrop, value, height, disabled, }: MapPinDropProps): React.JSX.Element;
231
+
232
+ interface SearchableInputItem {
233
+ value: string;
234
+ label: string;
235
+ description?: string;
236
+ icon?: ReactNode;
237
+ }
238
+ interface SearchableInputProps {
239
+ items: SearchableInputItem[];
240
+ value: string;
241
+ onSelect: (value: string) => void;
242
+ placeholder?: string;
243
+ disabled?: boolean;
244
+ isLoading?: boolean;
245
+ skeletonCount?: number;
246
+ className?: string;
247
+ onSearchChange?: (search: string) => void;
248
+ debounceMs?: number;
249
+ }
250
+ declare function SearchableInput({ items, value, onSelect, placeholder, disabled, isLoading, className, onSearchChange, debounceMs, }: SearchableInputProps): React.JSX.Element;
251
+
252
+ interface SearchableItem {
253
+ value: string;
254
+ label: string;
255
+ icon?: ReactNode;
256
+ description?: string;
257
+ }
258
+ interface SearchablePopoverProps {
259
+ items: SearchableItem[];
260
+ value: string;
261
+ onSelect: (value: string) => void;
262
+ placeholder?: string;
263
+ searchPlaceholder?: string;
264
+ emptyMessage?: string;
265
+ disabled?: boolean;
266
+ isLoading?: boolean;
267
+ skeletonCount?: number;
268
+ className?: string;
269
+ triggerClassName?: string;
270
+ contentClassName?: string;
271
+ width?: string;
272
+ onSearchChange?: (search: string) => void;
273
+ searchValue?: string;
274
+ hasNextPage?: boolean;
275
+ isFetchingNextPage?: boolean;
276
+ onFetchNextPage?: () => void;
277
+ resolvedSelectedItem?: SearchableItem;
278
+ }
279
+ declare function SearchablePopover({ items, value, onSelect, placeholder, searchPlaceholder, emptyMessage, disabled, isLoading, triggerClassName, contentClassName, onSearchChange, searchValue, hasNextPage, isFetchingNextPage, onFetchNextPage, resolvedSelectedItem, }: SearchablePopoverProps): React.JSX.Element;
280
+
281
+ interface LucideReactIconProps extends LucideProps {
282
+ icon: string | number | boolean | null | undefined | object;
283
+ }
284
+ declare const LucideReactIcon: React__default.FC<LucideReactIconProps>;
285
+
286
+ interface UseUnsavedChangesOptions {
287
+ browserMessage?: string;
288
+ dialogTitle?: string;
289
+ dialogBody?: string;
290
+ }
291
+ interface UnsavedChangesDialogProps {
292
+ open: boolean;
293
+ onStay: () => void;
294
+ onLeave: () => void;
295
+ title?: string;
296
+ body?: string;
297
+ }
298
+ interface UseUnsavedChangesReturn {
299
+ isDirty: boolean;
300
+ markDirty: () => void;
301
+ resetDirty: () => void;
302
+ guardedNavigate: (action: () => void) => void;
303
+ dialogProps: UnsavedChangesDialogProps;
304
+ }
305
+ declare function useUnsavedChanges(options?: UseUnsavedChangesOptions): UseUnsavedChangesReturn;
306
+ declare function UnsavedChangesDialog({ open, onStay, onLeave, title, body, }: UnsavedChangesDialogProps): React__default.JSX.Element | null;
307
+
308
+ interface SelectOption {
309
+ id: string;
310
+ name: string;
311
+ image_url?: string | null;
312
+ collection_type?: string;
313
+ [key: string]: unknown;
314
+ }
315
+ interface MultiSelectPickerProps {
316
+ selected: string[];
317
+ onChange: (items: string[]) => void;
318
+ options?: SelectOption[];
319
+ isLoading?: boolean;
320
+ isFetchingNextPage?: boolean;
321
+ hasNextPage?: boolean;
322
+ onFetchNextPage?: () => void;
323
+ placeholder?: string;
324
+ onCreateOption?: (name: string) => void;
325
+ onSearchChange?: (search: string) => void;
326
+ searchValue?: string;
327
+ }
328
+ declare function MultiSelectPicker({ selected, onChange, options, isLoading, isFetchingNextPage, hasNextPage, onFetchNextPage, placeholder, onCreateOption, onSearchChange, searchValue, }: MultiSelectPickerProps): React.JSX.Element;
329
+
330
+ type ObjectFit = "cover" | "contain" | "fill" | "none" | "scale-down";
331
+ interface BaseSmartImageProps {
332
+ src: string | null | undefined;
333
+ alt: string;
334
+ className?: string;
335
+ imageClassName?: string;
336
+ priority?: boolean;
337
+ isRelative?: boolean;
338
+ sizes?: string;
339
+ objectFit?: ObjectFit;
340
+ /**
341
+ * Optional fallback image src rendered instead of the broken-image
342
+ * placeholder if the primary src fails to load.
343
+ */
344
+ fallbackSrc?: string;
345
+ /**
346
+ * Passed-through to the underlying Next.js <Image> to suppress
347
+ * hydration warnings on SSR-rendered images.
348
+ */
349
+ suppressHydrationWarning?: boolean;
350
+ }
351
+ /**
352
+ * SmartImageProps for the standard (non-motion) variant.
353
+ * Adds optional `style` for container sizing (e.g. explicit width/height).
354
+ */
355
+ type SmartImageProps = BaseSmartImageProps & {
356
+ style?: CSSProperties;
357
+ draggable?: boolean;
358
+ };
359
+ /**
360
+ * SmartMotionImageProps for the Framer Motion variant.
361
+ * Extends SmartImageProps with all `motion.div` props (initial, animate, etc.)
362
+ */
363
+ type SmartMotionImageProps = BaseSmartImageProps & Omit<HTMLMotionProps<"div">, "draggable"> & {
364
+ style?: CSSProperties;
365
+ draggable?: boolean;
366
+ };
367
+ /**
368
+ * Standard smart image component for Kasuvia storefronts and dashboards.
369
+ *
370
+ * Wraps Next.js `<Image fill>` with:
371
+ * - Strict src validation (rejects null / empty / "null" strings)
372
+ * - A shimmer skeleton shown while the image loads
373
+ * - A labelled placeholder shown when there is no valid src
374
+ * - A distinct failed-to-load placeholder (or optional `fallbackSrc`)
375
+ * - An `objectFit` prop (defaults to "cover") for flexibility
376
+ *
377
+ * Peer dependencies: next, framer-motion, lucide-react
378
+ */
379
+ declare const SmartImage: React.ForwardRefExoticComponent<BaseSmartImageProps & {
380
+ style?: CSSProperties;
381
+ draggable?: boolean;
382
+ } & React.RefAttributes<HTMLImageElement>>;
383
+ /**
384
+ * Framer Motion variant of SmartImage.
385
+ * Accepts all `motion.div` props (e.g. `initial`, `animate`, `whileHover`)
386
+ * alongside the standard SmartImage props.
387
+ *
388
+ * Peer dependencies: next, framer-motion, lucide-react
389
+ */
390
+ declare const SmartMotionImage: React.ForwardRefExoticComponent<Omit<SmartMotionImageProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
391
+
392
+ interface ImageEditorModalProps {
393
+ open: boolean;
394
+ onOpenChange: (open: boolean) => void;
395
+ onImageSelect: (processedImage: Blob, fileName: string) => void;
396
+ aspectRatio?: number;
397
+ /** Max file size in bytes. Defaults to 10 MB. */
398
+ maxFileSize?: number;
399
+ acceptedFormats?: string[];
400
+ maxDimension?: number;
401
+ initialFile?: File | null;
402
+ /** Output blob format. Defaults to "image/jpeg". Pass "image/png" for alpha. */
403
+ outputFormat?: "image/jpeg" | "image/png";
404
+ /** JPEG export quality 0–1. */
405
+ outputQuality?: number;
406
+ /** Minimum output dimensions. */
407
+ minWidth?: number;
408
+ minHeight?: number;
409
+ }
410
+ /**
411
+ * ImageEditorModal — Studio-grade full-screen image editor.
412
+ *
413
+ * Features:
414
+ * - Fullscreen, theme-aware responsive modal layout
415
+ * - File drop / camera photo capture
416
+ * - Freehand / aspect-locked crop selection box with handles & rule-of-thirds grid
417
+ * - Pan, zoom, rotation slider, ±90° rotate buttons, horizontal & vertical flip
418
+ * - Brightness, contrast, saturation, blur adjustments
419
+ * - Offscreen canvas export slicing exact crop region into JPEG/PNG Blob
420
+ */
421
+ declare function ImageEditorModal({ open, onOpenChange, onImageSelect, aspectRatio: initialAspectRatio, maxFileSize, acceptedFormats, maxDimension, initialFile, outputFormat, outputQuality, minWidth, minHeight, }: ImageEditorModalProps): React.JSX.Element | null;
422
+
423
+ interface ImageViewerModalProps {
424
+ isOpen: boolean;
425
+ onClose: () => void;
426
+ images?: string[];
427
+ selectedImage: number;
428
+ setSelectedImage: (index: number) => void;
429
+ productName: string;
430
+ }
431
+ /**
432
+ * ImageViewerModal — Self-contained full-screen product image viewer.
433
+ *
434
+ * Designed to work standalone across any application (kasuvia-storefront, kasuvia, etc.)
435
+ * with zero reliance on external CSS purging or Tailwind utility scanning.
436
+ */
437
+ declare function ImageViewerModal({ isOpen, onClose, images, selectedImage, setSelectedImage, productName, }: ImageViewerModalProps): React.ReactPortal | null;
438
+
439
+ interface AnalyticsScriptsProps {
440
+ websiteId?: string;
441
+ scriptUrl?: string;
442
+ recorderUrl?: string;
443
+ }
444
+ /**
445
+ * AnalyticsScripts — Consent-gated Umami tracker + session-replay loader.
446
+ *
447
+ * REAL GAP FIXED (owner-requested legal-compliance review): Umami's tracker
448
+ * AND its session-replay/heatmap recorder used to render unconditionally the
449
+ * moment a project had analytics provisioned — no consent check anywhere.
450
+ * Session replay specifically is non-essential tracking under GDPR/NDPR
451
+ * consent rules regardless of PII-masking/sample-rate mitigations already
452
+ * built into the recorder's own config — masking reduces risk, it doesn't
453
+ * remove the consent requirement.
454
+ *
455
+ * This component is the actual technical gate: it renders nothing until the
456
+ * visitor has affirmatively accepted via CookieConsentBanner, and re-checks
457
+ * live if they change their choice without needing a page reload.
458
+ *
459
+ * Peer dependencies: next (next/script)
460
+ */
461
+ declare function AnalyticsScripts({ websiteId, scriptUrl, recorderUrl }: AnalyticsScriptsProps): React.JSX.Element | null;
462
+
463
+ interface CookieConsentBannerProps {
464
+ /**
465
+ * URL for the privacy policy page. Defaults to "/privacy".
466
+ */
467
+ privacyUrl?: string;
468
+ /**
469
+ * Custom className for the outer banner container.
470
+ */
471
+ className?: string;
472
+ /**
473
+ * Custom className for the "Accept" button.
474
+ */
475
+ acceptButtonClassName?: string;
476
+ /**
477
+ * Custom className for the "Reject non-essential" button.
478
+ */
479
+ rejectButtonClassName?: string;
480
+ }
481
+ /**
482
+ * CookieConsentBanner — GDPR/NDPR compliant consent gate.
483
+ *
484
+ * Owner-requested (legal-compliance review): analytics/session-replay tracking
485
+ * must not run before the visitor has actually chosen to allow it — see
486
+ * AnalyticsScripts, which gates the real script tags on this same stored choice.
487
+ *
488
+ * Shown once, bottom banner (not a blocking modal — the standard, least-intrusive
489
+ * pattern), with Accept and Reject weighted equally, matching GDPR/NDPR guidance
490
+ * against making "reject" harder to find than "accept".
491
+ *
492
+ * Styled with utility classes by default. Pass custom classNames to restyle with
493
+ * your storefront's theme (e.g. shadcn/Tailwind classes) without touching the SDK.
494
+ */
495
+ declare function CookieConsentBanner({ privacyUrl, className, acceptButtonClassName, rejectButtonClassName, }: CookieConsentBannerProps): React.JSX.Element | null;
496
+
497
+ export { AnalyticsScripts, type AnalyticsScriptsProps, BespokeAvailabilityBadge, type BespokeAvailabilityBadgeProps, BespokeProgressTracker, type BespokeProgressTrackerProps, type BespokeStageKey, CardSkeleton, type CardSkeletonProps, CartItem, type CartItemProps, CartItemSkeleton, type CartItemSkeletonProps, CartSidebar, type CartSidebarProps, CartTypeMismatchBanner, type CartTypeMismatchBannerProps, CookieConsentBanner, type CookieConsentBannerProps, DeliverableReviewer, type DeliverableReviewerProps, ImageEditorModal, type ImageEditorModalProps, ImageViewerModal, type ImageViewerModalProps, LucideReactIcon, type LucideReactIconProps, MapPinDrop, type MapPinDropProps, MultiSelectPicker, type MultiSelectPickerProps, OfflineBanner, type OfflineBannerProps, OrderItemSkeleton, type OrderItemSkeletonProps, ProductCard, type ProductCardProps, ProductCardSkeleton, type ProductCardSkeletonProps, ProductGrid, type ProductGridProps, SearchableInput, type SearchableInputItem, type SearchableInputProps, type SearchableItem, SearchablePopover, type SearchablePopoverProps, type SelectOption, SlotPicker, type SlotPickerProps, SmartImage, type SmartImageProps, SmartMotionImage, type SmartMotionImageProps, TableSkeleton, type TableSkeletonProps, TruncatedValue, type TruncatedValueProps, UnsavedChangesDialog, type UnsavedChangesDialogProps, type UseUnsavedChangesOptions, type UseUnsavedChangesReturn, useUnsavedChanges };