@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
@@ -4,67 +4,34 @@
4
4
  * Handles wishlist operations.
5
5
  * Stateless - all configuration passed as parameters.
6
6
  */
7
- import { GraphQLClient } from 'graphql-request'
8
7
  import type { SdkConfig } from '../config'
9
8
  import type { WishlistItem, WishlistListResponse, WishlistCheckResponse } from '../types/store'
10
-
11
- const GET_STOREFRONT_WISHLIST = `
12
- query GetStorefrontWishlist($page: Int!, $pageSize: Int!) {
13
- storefront_wishlist(page: $page, pageSize: $pageSize) {
14
- items {
15
- id
16
- product_id
17
- notes
18
- created_at
19
- product_name
20
- product_slug
21
- product_price
22
- currency
23
- product_image
24
- }
25
- total
26
- page
27
- page_size
28
- total_pages
29
- }
30
- }
31
- `
32
-
33
- const CHECK_IN_STOREFRONT_WISHLIST = `
34
- query CheckInStorefrontWishlist($productId: String!) {
35
- check_in_storefront_wishlist(productId: $productId) {
36
- in_wishlist
37
- }
38
- }
39
- `
40
-
41
- /**
42
- * Create GraphQL client with provided config
43
- */
44
- function createGraphQLClient(config: SdkConfig, accessToken?: string): GraphQLClient {
45
- const headers: Record<string, string> = {
46
- 'Content-Type': 'application/json',
47
- 'Accept': 'application/json',
48
- 'X-Storefront-Key': config.storefrontApiKey,
49
- 'X-Business-ID': config.businessId,
50
- }
51
- if (accessToken) {
52
- headers['Authorization'] = `Bearer ${accessToken}`
53
- }
54
- return new GraphQLClient(`${config.backendUrls.store}/graphql`, { headers })
55
- }
9
+ import { createGraphQLClient } from '../utils/graphql-url'
10
+ import { GetStorefrontWishlistDocument, CheckInStorefrontWishlistDocument } from '../generated/graphql'
11
+ import { apiRequest, authHeaders, JSON_HEADERS } from '../utils/http-client'
56
12
 
57
13
  /**
58
14
  * Fetch the customer's wishlist for the current business.
59
15
  */
60
16
  export async function getWishlist(config: SdkConfig, accessToken: string, params?: { page?: number; pageSize?: number }): Promise<WishlistListResponse> {
61
17
  const client = createGraphQLClient(config, accessToken)
62
- const variables = {
18
+ const data = await client.request(GetStorefrontWishlistDocument, {
63
19
  page: params?.page || 1,
64
20
  pageSize: params?.pageSize || 20,
21
+ })
22
+ return {
23
+ ...data.storefront_wishlist,
24
+ items: data.storefront_wishlist.items.map((item) => ({
25
+ ...item,
26
+ created_at: item.created_at as string,
27
+ notes: item.notes ?? undefined,
28
+ product_name: item.product_name ?? undefined,
29
+ product_slug: item.product_slug ?? undefined,
30
+ product_price: item.product_price ?? undefined,
31
+ currency: item.currency ?? undefined,
32
+ product_image: item.product_image ?? undefined,
33
+ })) satisfies WishlistItem[],
65
34
  }
66
- const data = await client.request(GET_STOREFRONT_WISHLIST, variables)
67
- return data.storefront_wishlist
68
35
  }
69
36
 
70
37
  /**
@@ -72,7 +39,7 @@ export async function getWishlist(config: SdkConfig, accessToken: string, params
72
39
  */
73
40
  export async function checkInWishlist(config: SdkConfig, accessToken: string, productId: string): Promise<WishlistCheckResponse> {
74
41
  const client = createGraphQLClient(config, accessToken)
75
- const data = await client.request(CHECK_IN_STOREFRONT_WISHLIST, { productId })
42
+ const data = await client.request(CheckInStorefrontWishlistDocument, { productId })
76
43
  return data.check_in_storefront_wishlist
77
44
  }
78
45
 
@@ -80,49 +47,35 @@ export async function checkInWishlist(config: SdkConfig, accessToken: string, pr
80
47
  * Add a product to the customer's wishlist.
81
48
  */
82
49
  export async function addToWishlist(config: SdkConfig, accessToken: string, productId: string, notes?: string): Promise<WishlistItem> {
83
- const response = await fetch(`${config.backendUrls.store}/wishlist/`, {
50
+ return apiRequest<WishlistItem>(`${config.backendUrls.store}/wishlist/`, {
84
51
  method: 'POST',
85
- headers: {
86
- 'Content-Type': 'application/json',
87
- 'Authorization': `Bearer ${accessToken}`,
88
- "X-Storefront-Key": config.storefrontApiKey,
89
- "X-Business-ID": config.businessId,
90
- },
52
+ headers: { ...JSON_HEADERS, ...authHeaders(config, accessToken) },
91
53
  body: JSON.stringify({
92
54
  product_id: productId,
93
55
  notes: notes || undefined,
94
56
  }),
57
+ fallbackErrorMessage: 'Failed to add to wishlist',
95
58
  })
96
- if (!response.ok) throw new Error('Failed to add to wishlist')
97
- return response.json()
98
59
  }
99
60
 
100
61
  /**
101
62
  * Remove an item from the customer's wishlist by item ID.
102
63
  */
103
64
  export async function removeFromWishlist(config: SdkConfig, accessToken: string, itemId: string): Promise<void> {
104
- const response = await fetch(`${config.backendUrls.store}/wishlist/${itemId}`, {
65
+ await apiRequest(`${config.backendUrls.store}/wishlist/${itemId}`, {
105
66
  method: 'DELETE',
106
- headers: {
107
- 'Authorization': `Bearer ${accessToken}`,
108
- "X-Storefront-Key": config.storefrontApiKey,
109
- "X-Business-ID": config.businessId,
110
- },
67
+ headers: authHeaders(config, accessToken),
68
+ fallbackErrorMessage: 'Failed to remove from wishlist',
111
69
  })
112
- if (!response.ok) throw new Error('Failed to remove from wishlist')
113
70
  }
114
71
 
115
72
  /**
116
73
  * Remove a product from the customer's wishlist by product ID.
117
74
  */
118
75
  export async function removeProductFromWishlist(config: SdkConfig, accessToken: string, productId: string): Promise<void> {
119
- const response = await fetch(`${config.backendUrls.store}/wishlist/product/${productId}`, {
76
+ await apiRequest(`${config.backendUrls.store}/wishlist/product/${productId}`, {
120
77
  method: 'DELETE',
121
- headers: {
122
- 'Authorization': `Bearer ${accessToken}`,
123
- "X-Storefront-Key": config.storefrontApiKey,
124
- "X-Business-ID": config.businessId,
125
- },
78
+ headers: authHeaders(config, accessToken),
79
+ fallbackErrorMessage: 'Failed to remove product from wishlist',
126
80
  })
127
- if (!response.ok) throw new Error('Failed to remove product from wishlist')
128
81
  }
@@ -22,6 +22,7 @@ export interface CustomerProfileFields {
22
22
  }
23
23
 
24
24
  export interface CustomerUser {
25
+ /** Scoped to this business only — the same email is a fully separate identity (and id) at any other business/storefront. Never a platform-wide user id. */
25
26
  id: string;
26
27
  email: string;
27
28
  firstname?: string;
@@ -51,6 +52,7 @@ export interface UpdateProfilePayload {
51
52
  firstname?: string;
52
53
  lastname?: string;
53
54
  phone?: string;
55
+ profile_picture?: string;
54
56
  }
55
57
 
56
58
  export interface NotificationPreferences {
@@ -73,24 +75,23 @@ export interface NotificationPreferences {
73
75
  sms_delivery_updates: boolean;
74
76
  }
75
77
 
76
-
77
- // ── Business Context (storefront is scoped to one business) ──────────────────
78
-
79
- export interface BusinessContext {
80
- id: string; // UUID
81
- business_id: string; // B-XXXXXXXX — permanent, never changes
82
- name: string;
83
- logo_picture?: string | null;
84
- display_currency?: string;
85
- }
86
-
87
78
  // ── API Responses ─────────────────────────────────────────────────────────────
88
79
 
89
80
  export interface CustomerAuthResponse {
90
81
  access: string;
91
82
  refresh: string;
92
83
  user: CustomerUser;
93
- business: BusinessContext;
84
+ // REAL BUG FIXED (2026-09-20): the backend used to embed the FULL business
85
+ // record here (tax_id, settlement_currency, notification_preferences, the
86
+ // full plan, postal_code, internal UUID `id`) reaching every customer's
87
+ // browser on login/register/passwordless-verify. Now uses the same
88
+ // CustomerFacingBusinessSerializer allow-list as GET /auth/storefront/
89
+ // business-info/ — identical shape, so reuse StorefrontBusinessInfo rather
90
+ // than keep a second, drifted type (the old `BusinessContext` type here
91
+ // had `id`/`display_currency` fields that never matched what the backend
92
+ // actually returned either, and had zero consumers anywhere in the SDK or
93
+ // storefront — confirmed via grep before removing it).
94
+ business: StorefrontBusinessInfo;
94
95
  is_new?: boolean;
95
96
  }
96
97
 
@@ -101,22 +102,36 @@ export interface RefreshTokenResponse {
101
102
 
102
103
  // ── Request Payloads ──────────────────────────────────────────────────────────
103
104
 
104
- /** POST /auth/login/customer/ — field name is `tenant_id` (the business_id) */
105
+ /**
106
+ * POST /auth/login/customer/ — business is identified by the
107
+ * X-Storefront-Key + X-Business-ID headers (config.storefrontApiKey /
108
+ * config.businessId), not a body field (see plan: Customer Storefront
109
+ * Identity Enforcement).
110
+ */
105
111
  export interface CustomerLoginPayload {
106
- tenant_id: string; // config.api.storefrontBusinessId
107
112
  email: string;
108
113
  password: string;
109
114
  }
110
115
 
111
- /** POST /auth/register/customer/ */
116
+ /** POST /auth/register/customer/ — business identified via headers, see CustomerLoginPayload. */
112
117
  export interface CustomerRegisterPayload {
113
- tenant_id: string; // config.api.storefrontBusinessId
114
118
  first_name?: string;
115
119
  last_name?: string;
116
120
  email: string;
117
121
  password: string;
118
122
  }
119
123
 
124
+ /** POST /auth/login/customer/passwordless/request/ — business identified via headers, see CustomerLoginPayload. */
125
+ export interface CustomerPasswordlessRequestPayload {
126
+ email: string;
127
+ }
128
+
129
+ /** POST /auth/login/customer/passwordless/verify/ — business identified via headers, see CustomerLoginPayload. */
130
+ export interface CustomerPasswordlessVerifyPayload {
131
+ email: string;
132
+ code: string;
133
+ }
134
+
120
135
  /** POST /auth/verify-otp/ */
121
136
  export interface OTPVerifyPayload {
122
137
  email: string;
@@ -134,6 +149,126 @@ export interface LogoutPayload {
134
149
  refresh?: string;
135
150
  }
136
151
 
152
+ /** POST /auth/forgot-password/customer/ */
153
+ export interface CustomerForgotPasswordPayload {
154
+ email: string;
155
+ }
156
+
157
+ /** POST /auth/reset-password/customer/ */
158
+ export interface CustomerResetPasswordPayload {
159
+ email: string;
160
+ code: string;
161
+ new_password: string;
162
+ }
163
+
164
+ /** POST /auth/customer/deactivate/ */
165
+ export interface CustomerDeactivatePayload {
166
+ reason?: string;
167
+ confirmation_code?: string;
168
+ }
169
+
170
+ /**
171
+ * The real backend (require_destructive_confirmation, kasuvia-auth/common/
172
+ * utils/destructive_confirmation.py:76) ALWAYS returns this shape with
173
+ * HTTP 409 on the first deactivate call (no confirmation_code yet) — this
174
+ * is not an error case, it's step 1 of a required two-step confirmation.
175
+ * The code is generated and returned by the server itself (not sent via
176
+ * email) — it exists to require an explicit second, deliberate action in
177
+ * the UI, not to verify identity out-of-band. Resubmit the identical
178
+ * request with `confirmation_code` set to this value to actually deactivate.
179
+ */
180
+ export interface DeactivateConfirmationRequired {
181
+ message: string;
182
+ impact: string[];
183
+ confirmation_code: string;
184
+ expires_in_minutes: number;
185
+ }
186
+
187
+ export type DeactivateAccountResult =
188
+ | { status: 'confirmation_required'; data: DeactivateConfirmationRequired }
189
+ | { status: 'success'; detail: string };
190
+
191
+ /** POST /auth/change-password/ — identity-mode-agnostic (owner/staff/customer). */
192
+ export interface ChangePasswordPayload {
193
+ /** Omit only when the account has no password yet (social/passwordless-only). */
194
+ old_password?: string;
195
+ new_password: string;
196
+ confirm_password: string;
197
+ }
198
+
199
+ /** POST /auth/customer/reactivate/ */
200
+ export interface CustomerReactivatePayload {
201
+ email: string;
202
+ }
203
+
204
+ /** POST /auth/customer/reactivate/verify/ */
205
+ export interface CustomerReactivateVerifyPayload {
206
+ email: string;
207
+ code: string;
208
+ }
209
+
210
+ /**
211
+ * One entry in a customer's saved-address book.
212
+ * GET/POST /auth/profile/addresses/, PATCH/DELETE /auth/profile/addresses/{id}/.
213
+ * Field names mirror `DeliveryAddress`/`ShippingLocation` exactly so a saved
214
+ * address can be used directly at checkout with no remapping.
215
+ */
216
+ export interface CustomerAddress {
217
+ id: string;
218
+ label: string;
219
+ first_name: string;
220
+ last_name: string;
221
+ phone: string;
222
+ street: string;
223
+ city: string;
224
+ state: string;
225
+ lga: string;
226
+ country: string;
227
+ postal: string;
228
+ is_default: boolean;
229
+ created_at: string;
230
+ }
231
+
232
+ /** Fields a caller may set when creating a saved address - `id`/`created_at` are server-assigned. */
233
+ export type CreateCustomerAddressPayload = Omit<CustomerAddress, "id" | "created_at">;
234
+
235
+ /** Partial update to an existing saved address. */
236
+ export type UpdateCustomerAddressPayload = Partial<CreateCustomerAddressPayload>;
237
+
238
+ /**
239
+ * GET /auth/storefront/business-info/ response.
240
+ * Public, unauthenticated (storefront-key-gated) business info for a
241
+ * storefront to render — e.g. footer/about section. An explicit allow-list
242
+ * on the backend (kasuvia-auth's PublicBusinessInfoView) — never owner
243
+ * identity, financial, or internal integration/logistics fields.
244
+ */
245
+ export interface StorefrontBusinessInfo {
246
+ business_id: string;
247
+ name: string;
248
+ // CharField(max_length=140, blank=True, null=True) — real null on the wire
249
+ // when never set, unlike the blank=True-only fields below.
250
+ tagline: string | null;
251
+ business_type: string;
252
+ country: string;
253
+ // CharField/EmailField/TextField(blank=True) — always a string on the
254
+ // wire (possibly empty ""), never null. Only logo_picture is a real
255
+ // URLField(null=True) below.
256
+ business_contact_phone: string;
257
+ business_contact_email: string;
258
+ business_physical_address: string;
259
+ logo_picture: string | null;
260
+ social_links: Record<string, string>;
261
+ currency: string;
262
+ // Optional, business-chosen hex colors (e.g. "#RRGGBB") — null when never
263
+ // set. Added to PublicBusinessInfoView's allow-list 2026-09-22; previously
264
+ // stored on the backend but never returned here.
265
+ brand_color_primary: string | null;
266
+ brand_color_secondary: string | null;
267
+ brand_color_accent: string | null;
268
+ brand_color_tertiary: string | null;
269
+ }
270
+
271
+
137
272
  // ── Auth Context ──────────────────────────────────────────────────────────────
138
273
 
139
274
  export interface AuthContextValue {
@@ -144,6 +279,14 @@ export interface AuthContextValue {
144
279
  closeModal: () => void;
145
280
  setCustomer: (user: CustomerUser | null) => void;
146
281
  logout: () => Promise<void>;
147
- login: (payload: Omit<CustomerLoginPayload, 'tenant_id'>) => Promise<void>;
148
- register: (payload: Omit<CustomerRegisterPayload, 'tenant_id'>) => Promise<{ requires_verification: boolean }>;
282
+ login: (payload: CustomerLoginPayload) => Promise<void>;
283
+ // REAL BUG FIXED (2026-09-20): `requires_verification` was never a real
284
+ // backend concept for customers — CustomerRegisterView never sends an
285
+ // email OTP and registration is instant. Removed rather than kept as an
286
+ // always-false field.
287
+ register: (payload: CustomerRegisterPayload) => Promise<void>;
288
+ /** Current browser's guest cart session id — pass through to any auth call (e.g. OTP verify) not already routed through login()/register(), so kasuvia-auth's server-side post-login cart merge (see loginCustomer's doc comment) actually fires. */
289
+ guestSessionId: string | null;
290
+ /** Call after any auth flow not routed through login()/register() succeeds (e.g. OTP verify), once the server-side merge above has run — rotates to a fresh guest session id. */
291
+ rotateGuestSessionId: () => void;
149
292
  }
@@ -1,3 +1,10 @@
1
+ // REAL BUG FIXED (2026-09-21): `is_verified` was never a real field on this
2
+ // endpoint's response — kasuvia-auth's InternalBusinessDetailView
3
+ // (internal.py) explicitly renamed it away (its own comment: "REAL BUG
4
+ // FOUND AND FIXED... named 'is_verified' but meant 'has a name and is
5
+ // active'... Renamed to what it actually checks") to the two real fields
6
+ // below well before this SDK type was last touched. Not read by any current
7
+ // storefront consumer, but a landmine for the next one.
1
8
  export interface BusinessConfig {
2
9
  business_id: string;
3
10
  storefront_business_id: string;
@@ -6,6 +13,7 @@ export interface BusinessConfig {
6
13
  settlement_currency: string;
7
14
  country: string;
8
15
  region: string;
9
- is_verified: boolean;
16
+ has_completed_onboarding: boolean;
17
+ owner_kyc_verified: boolean;
10
18
  is_active: boolean;
11
19
  }
@@ -9,7 +9,7 @@ export interface CmsField {
9
9
  label: string
10
10
  type: string
11
11
  required: boolean
12
- config: Record<string, any>
12
+ config: Record<string, unknown>
13
13
  sub_fields?: CmsField[]
14
14
  }
15
15
 
@@ -38,17 +38,102 @@ export interface CmsEntry {
38
38
  id: string
39
39
  business_id: string
40
40
  model_slug: string
41
- data: Record<string, any>
41
+ data: Record<string, unknown>
42
42
  created_at: string
43
43
  updated_at: string
44
44
  created_by: string | null
45
45
  }
46
46
 
47
+ /** Real value shape of a CmsField with type "media" (kasuvia-media-system asset reference). */
48
+ export interface CmsMediaValue {
49
+ id: string
50
+ url: string
51
+ alt?: string
52
+ }
53
+
54
+ /** Real value shape of a CmsField with type "icon". */
55
+ export interface CmsIconValue {
56
+ package: string
57
+ name: string
58
+ }
59
+
60
+ /**
61
+ * The "blogs" CMS model's real field shape, as created by a merchant through
62
+ * kasuvia-cms's dynamic model builder. Field names are merchant-defined
63
+ * (this is a dynamic CMS, not a fixed schema) — this type documents the
64
+ * specific field set kasuvia-storefront's dedicated /blog pages render,
65
+ * confirmed live against a real "blogs" model. A storefront whose merchant
66
+ * named these fields differently needs its own type — this is a rendering
67
+ * convenience, not a backend contract.
68
+ */
69
+ export interface BlogEntryData {
70
+ blog_title: string
71
+ blog_slug: string
72
+ blog_content: string
73
+ excerpt?: string
74
+ author: string
75
+ category: string
76
+ tags?: string
77
+ featured_image?: CmsMediaValue
78
+ published_date: string
79
+ is_published: boolean
80
+ is_featured?: boolean
81
+ featured_icon?: CmsIconValue
82
+ }
83
+
84
+ /**
85
+ * The "store-locations" CMS model's real field shape (kasuvia-cms/scripts/
86
+ * seed_dummy_models.py's STORE_LOCATIONS_MODEL — merchant-defined, dynamic
87
+ * CMS, this documents the real shape as configured, not a fixed schema).
88
+ */
89
+ export interface StoreLocationEntryData {
90
+ store_name: string
91
+ address: string
92
+ city: string
93
+ store_type: 'Flagship' | 'Outlet' | 'Pop-up' | 'Franchise'
94
+ phone?: string
95
+ is_open: boolean
96
+ opening_time: string
97
+ closing_time: string
98
+ store_image?: CmsMediaValue
99
+ store_icon?: CmsIconValue
100
+ }
101
+
102
+ /** The "flash-sales" CMS model's real field shape (same source as above). */
103
+ export interface FlashSaleEntryData {
104
+ event_title: string
105
+ event_description: string
106
+ start_date: string
107
+ end_date: string
108
+ countdown_datetime: string
109
+ discount_percentage: number
110
+ is_active: boolean
111
+ banner_image?: CmsMediaValue
112
+ }
113
+
114
+ /** The "product-faqs" CMS model's real field shape (same source as above). */
115
+ export interface ProductFaqEntryData {
116
+ category: string
117
+ question: string
118
+ answer: string
119
+ is_featured: boolean
120
+ /** Free-text merchant-typed field (e.g. "Shipping Policy: /policies/shipping, Track Your Order: /orders/track") — not structured, rendered as plain text. */
121
+ related_links?: string
122
+ }
123
+
124
+ // REAL BUG FIXED (2026-09-21): this used {entries, total, page, page_size} —
125
+ // kasuvia-cms's own real response (cms_schemas.py's CmsEntryListResponse)
126
+ // was renamed to the platform-standard paginated envelope
127
+ // {results, count, page, page_size, total_pages} back in 2026-09-17, with
128
+ // an explicit code comment flagging kasuvia-sdk/kasuvia-storefront as not
129
+ // yet updated (they were frozen at the time). Every CMS list call has been
130
+ // silently reading undefined `.entries`/`.total` since.
47
131
  export interface CmsEntryListResponse {
48
- entries: CmsEntry[]
49
- total: number
132
+ results: CmsEntry[]
133
+ count: number
50
134
  page: number
51
135
  page_size: number
136
+ total_pages: number
52
137
  }
53
138
 
54
139
  export interface CmsIcon {
@@ -69,9 +154,9 @@ export interface ListCmsEntriesParams {
69
154
  }
70
155
 
71
156
  export interface CreateCmsEntryParams {
72
- data: Record<string, any>
157
+ data: Record<string, unknown>
73
158
  }
74
159
 
75
160
  export interface UpdateCmsEntryParams {
76
- data: Record<string, any>
161
+ data: Record<string, unknown>
77
162
  }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Legal Document Types
3
+ *
4
+ * Types for merchant storefront legal pages generated by
5
+ * kasuvia-website-builder-system via kasuvia-cms.
6
+ */
7
+
8
+ export interface LegalSection {
9
+ heading: string;
10
+ body: string;
11
+ }
12
+
13
+ export interface MerchantLegalDocument {
14
+ document_type: string;
15
+ template_version: string;
16
+ rendered_sections: LegalSection[];
17
+ published_at: string;
18
+ }
@@ -1,74 +1,15 @@
1
1
  /**
2
- * Consultation types for customer-facing storefront
2
+ * Bespoke commission types for the customer-facing storefront.
3
+ *
4
+ * REAL BUG FIXED (2026-09-21): this file used to also declare a fictional
5
+ * "Consultation" resource (ConsultationStatus/Consultation/ConsultationMessage/
6
+ * etc.) — confirmed there is no /social/consultations/* route or Consultation
7
+ * model anywhere in kasuvia-store-management. The owner confirmed the
8
+ * feature no longer exists there. Removed along with its dead SDK service,
9
+ * hooks, and storefront pages/components — only the real bespoke types
10
+ * (used by bespoke.service.ts against real backend endpoints) remain here.
3
11
  */
4
12
 
5
- export type ConsultationStatus =
6
- | "pending"
7
- | "quoted"
8
- | "negotiating"
9
- | "deposit_pending"
10
- | "deposit_paid"
11
- | "converting"
12
- | "converted"
13
- | "rejected";
14
-
15
- export type ConsultationProductType = "bespoke" | "service";
16
-
17
- export interface ConsultationMessage {
18
- id: string;
19
- consultation_id: string;
20
- sender_id: string;
21
- sender_role: "customer" | "merchant" | "system";
22
- message: string;
23
- attachments: string[];
24
- is_read: boolean;
25
- created_at: string;
26
- }
27
-
28
- export interface Consultation {
29
- id: string;
30
- business_id: string;
31
- customer_id: string;
32
- product_id: string | null;
33
- title: string;
34
- status: ConsultationStatus;
35
- product_type: ConsultationProductType;
36
- quote_amount: number | null;
37
- agreed_price: number | null;
38
- deposit_amount: number | null;
39
- deposit_required: boolean;
40
- is_physical_delivery: boolean;
41
- expected_delivery_type: string | null;
42
- structured_details: Record<string, unknown>;
43
- rejection_reason: string | null;
44
- converted_order_id: string | null;
45
- slot_booking_id: string | null;
46
- capacity_config_id: string | null;
47
- created_at: string;
48
- updated_at: string;
49
- messages: ConsultationMessage[];
50
- }
51
-
52
- export interface ConsultationCreatePayload {
53
- product_id?: string;
54
- title: string;
55
- description: string;
56
- reference_images?: string[];
57
- custom_measurements?: Record<string, string | number>;
58
- preferred_timeline?: string;
59
- special_instructions?: string;
60
- product_type?: ConsultationProductType;
61
- }
62
-
63
- export interface ConsultationMessagePayload {
64
- message: string;
65
- attachments?: string[];
66
- }
67
-
68
- export interface ConsultationQuoteAcceptPayload {
69
- agreed_price: number;
70
- }
71
-
72
13
  export interface BespokeAvailability {
73
14
  available_slots: number;
74
15
  next_available_date: string | null;
@@ -77,3 +18,54 @@ export interface BespokeAvailability {
77
18
  production_days: number;
78
19
  is_available: boolean;
79
20
  }
21
+
22
+ /**
23
+ * Returned by all 3 bespoke deliverable-proofing action endpoints (POST
24
+ * .../bespoke/submit-deliverable, .../approve, .../request-revision) —
25
+ * matches BespokeStatusResponse (kasuvia-store-management/app/schemas/
26
+ * bespoke.py:120-128) exactly.
27
+ */
28
+ export interface BespokeDeliverableStatus {
29
+ id: string;
30
+ order_status: string;
31
+ bespoke_status: string;
32
+ bespoke_deliverable_submitted_at: string | null;
33
+ bespoke_deliverable_url: string | null;
34
+ bespoke_deliverable_notes: string | null;
35
+ bespoke_buyer_approved_at: string | null;
36
+ bespoke_revision_count: number;
37
+ /**
38
+ * REAL GAP FIXED (2026-09-23) via the REST codegen initiative: the real
39
+ * backend response (BespokeStatusResponse, kasuvia-store-management's
40
+ * app/schemas/bespoke.py) has always included this field - this hand-
41
+ * written interface simply never declared it, so it was silently
42
+ * unavailable to every consumer even though the backend was already
43
+ * returning it on every deliverable-status response.
44
+ */
45
+ bespoke_revision_notes: string | null;
46
+ }
47
+
48
+ /**
49
+ * Embedded on an Order as `order.bespoke_fulfillment` — matches
50
+ * BespokeFulfillmentResponse (kasuvia-store-management/app/schemas/
51
+ * order.py:150-168) exactly. This is the real source of a bespoke order's
52
+ * current deliverable state; there is no separate list/GET endpoint for it.
53
+ */
54
+ export interface BespokeFulfillment {
55
+ brief: string | null;
56
+ reference_images: string[] | null;
57
+ measurements: Record<string, unknown> | null;
58
+ preferred_timeline: string | null;
59
+ special_instructions: string | null;
60
+ deposit_ratio: number | null;
61
+ deposit_amount: number | null;
62
+ balance_amount: number | null;
63
+ status: string | null;
64
+ deliverable_submitted_at: string | null;
65
+ deliverable_url: string | null;
66
+ deliverable_notes: string | null;
67
+ buyer_approved_at: string | null;
68
+ revision_count: number;
69
+ revision_notes: string | null;
70
+ timeline_breached_at: string | null;
71
+ }