@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
@@ -1,9 +1,18 @@
1
1
  import type { DeliveryAddress } from "./shipping";
2
2
 
3
3
  export * from "./shipping";
4
+ export * from "./invoice";
5
+ export * from "./checkout";
4
6
  export type { DeliveryAddress };
5
7
 
6
- export type ProductType = "physical" | "digital" | "service" | "bespoke";
8
+ export enum ProductTypeEnum {
9
+ PHYSICAL = "physical",
10
+ DIGITAL = "digital",
11
+ SERVICE = "service",
12
+ BESPOKE = "bespoke",
13
+ }
14
+
15
+ export type ProductType = ProductTypeEnum | "physical" | "digital" | "service" | "bespoke";
7
16
 
8
17
  export enum ServiceTypeEnum {
9
18
  APPOINTMENT = 'appointment',
@@ -13,13 +22,18 @@ export enum ServiceTypeEnum {
13
22
 
14
23
  export type ServiceType = ServiceTypeEnum | 'appointment' | 'project' | 'recurring'
15
24
 
25
+ // REAL BUG FIXED (2026-09-21): these values didn't match anything the real
26
+ // backend ever stores — kasuvia-store-management's Product.service_location_type
27
+ // (app/models/catalog.py) is validated against exactly "remote" |
28
+ // "at_business" | "customer_location". The old "physical"/"both" values
29
+ // would never match a real product's actual value.
16
30
  export enum ServiceLocationTypeEnum {
17
31
  REMOTE = 'remote',
18
- PHYSICAL = 'physical',
19
- BOTH = 'both',
32
+ AT_BUSINESS = 'at_business',
33
+ CUSTOMER_LOCATION = 'customer_location',
20
34
  }
21
35
 
22
- export type ServiceLocationType = ServiceLocationTypeEnum | 'remote' | 'physical' | 'both'
36
+ export type ServiceLocationType = ServiceLocationTypeEnum | 'remote' | 'at_business' | 'customer_location'
23
37
 
24
38
  export enum ServiceRecurringIntervalEnum {
25
39
  WEEKLY = 'weekly',
@@ -43,9 +57,11 @@ export interface Collection {
43
57
  name: string
44
58
  slug: string
45
59
  description?: string
46
- image?: string
47
60
  collection_type?: string
48
- rules?: Record<string, unknown>
61
+ /** Raw JSON string of smart-collection matching rules (kasuvia-store-management's own evaluation detail, never parsed client-side) — the real backend GraphQL field is a String scalar, not a structured object. */
62
+ rules?: string
63
+ image_url?: string
64
+ image_id?: string
49
65
  status?: string
50
66
  sort_order?: number
51
67
  created_at: string
@@ -62,9 +78,9 @@ export interface Inventory {
62
78
  reorder_quantity: number
63
79
  track_quantity: boolean
64
80
  allow_backorder: boolean
65
- available: number
66
- is_in_stock: boolean
67
- is_low_stock: boolean
81
+ available?: number
82
+ is_in_stock?: boolean
83
+ is_low_stock?: boolean
68
84
  updated_at: string
69
85
  }
70
86
 
@@ -76,33 +92,47 @@ export interface ProductVariant {
76
92
  price?: number
77
93
  images: string[]
78
94
  weight_kg?: number
95
+ length_cm?: number
96
+ width_cm?: number
97
+ height_cm?: number
79
98
  is_active: boolean
80
99
  inventory?: Inventory
81
- created_at: string
82
- updated_at: string
83
100
  }
84
101
 
85
102
  export interface CustomField {
86
103
  label: string
87
104
  unit?: string
88
105
  required: boolean
89
- type?: string
90
- key?: string
91
- placeholder?: string
106
+ /** "text" (free text/number input) or "choice" (customer picks from
107
+ * `options`) — matches kasuvia-store-management's CustomFieldSchema. */
108
+ field_type: "text" | "choice"
109
+ /** Only meaningful when field_type is "choice" - the pick list a
110
+ * merchant configured. Always [] for a "text" field. */
111
+ options: string[]
112
+ }
113
+
114
+ /** One file of a DIGITAL/MIXED product (kasuvia-store-management's ProductDigitalFile) — replaces the old dropped single `digital_file_key` scalar. */
115
+ export interface ProductDigitalFile {
116
+ id: string
117
+ file_key: string
118
+ display_name: string
119
+ sort_order: number
92
120
  }
93
121
 
94
122
  export interface Product {
95
123
  id: string
96
124
  business_id: string
97
125
  name: string
98
- slug: string
126
+ slug?: string
99
127
  description?: string
100
128
  brand?: string
101
129
  product_type: ProductType
102
130
  requires_shipping: boolean
103
131
  is_consumable: boolean
104
132
  is_bespoke: boolean
105
- is_physical?: boolean | null // For bespoke products: indicates if custom item requires physical delivery
133
+ is_physical?: boolean
134
+ is_non_returnable?: boolean
135
+ deposit_percentage?: number
106
136
  base_price: number
107
137
  currency: string
108
138
  sale_price?: number
@@ -110,42 +140,67 @@ export interface Product {
110
140
  sale_end?: string
111
141
  images: string[]
112
142
  tags: string[]
113
- custom_fields: CustomField[]
143
+ custom_fields?: CustomField[]
114
144
  is_published: boolean
115
145
  is_featured: boolean
116
146
  is_popular: boolean
117
147
  is_new: boolean
118
148
  is_temporary: boolean
119
- digital_file_key?: string
149
+ /** Real fields (kasuvia-crm's review pipeline syncs into these). */
150
+ average_rating?: number | null
151
+ rating_count: number
152
+ /** "kasuvia" for organic products, a real channel key ("whatsapp", etc.) for channel-imported products. */
153
+ source_channel: string
154
+ source_channel_ref_id?: string | null
155
+ digital_files: ProductDigitalFile[]
120
156
  download_limit?: number
121
157
  download_expires_at?: string
122
- // Service-specific fields
123
- service_type?: ServiceType | null
124
- service_duration_minutes?: number | null
125
- service_location_type?: ServiceLocationType | null
158
+ service_type?: ServiceType
159
+ service_duration_minutes?: number
160
+ service_location_type?: ServiceLocationType
161
+ /** Only meaningful when service_location_type === "customer_location". */
162
+ service_coverage_radius_km?: number | null
126
163
  service_requires_consultation?: boolean
127
- service_max_capacity_per_slot?: number | null
164
+ service_max_capacity_per_slot?: number
128
165
  service_buffer_minutes?: number
129
166
  service_is_recurring?: boolean
130
- service_recurring_interval?: ServiceRecurringInterval | null
167
+ service_recurring_interval?: ServiceRecurringInterval
131
168
  service_project_based?: boolean
132
- weight_kg: number
169
+ weight_kg?: number
133
170
  length_cm?: number
134
171
  width_cm?: number
135
172
  height_cm?: number
173
+ variants?: ProductVariant[]
174
+ collections?: Collection[]
175
+ inventory?: Inventory
136
176
  created_at: string
137
177
  updated_at: string
138
- variants: ProductVariant[]
139
- collections: Collection[]
178
+ }
179
+
180
+ export interface ProductListResponse {
181
+ items: Product[]
182
+ total: number
183
+ page: number
184
+ page_size: number
185
+ total_pages: number
186
+ }
187
+
188
+ export interface CollectionListResponse {
189
+ items: Collection[]
190
+ total: number
191
+ page: number
192
+ page_size: number
193
+ total_pages: number
140
194
  }
141
195
 
142
196
  export interface ListParams {
143
197
  page?: number
144
198
  pageSize?: number
145
- search?: string
146
- ordering?: string
147
199
  collectionSlug?: string
200
+ search?: string
148
201
  excludeProductId?: string
202
+ /** One of physical/digital/service/bespoke — omit to browse the whole catalog. */
203
+ productType?: ProductType
149
204
  }
150
205
 
151
206
  export interface PaginatedResponse<T> {
@@ -165,23 +220,29 @@ export interface CartItem {
165
220
  product_id: string
166
221
  product_slug: string
167
222
  product_name: string
168
- image?: string
223
+ image_url?: string
169
224
  variant_id?: string
170
225
  variant_attributes?: string | Record<string, string>
171
226
  unit_price: number
172
227
  quantity: number
173
228
  custom_measurements?: string
174
229
  is_bespoke: boolean
230
+ product_type?: ProductType
175
231
  created_at: string
176
232
  updated_at: string
177
233
  }
178
234
 
179
235
  export interface CartResponse {
236
+ /**
237
+ * The Redis cart key returned by kasuvia-cart-management on every cart operation.
238
+ * Used to correlate a cart with a shipping-rate quote and checkout session.
239
+ */
240
+ id?: string
180
241
  items: CartItem[]
181
242
  subtotal: number
182
243
  total: number
183
244
  item_count: number
184
- product_type?: string // "physical", "digital", "bespoke"
245
+ product_type?: ProductType
185
246
  }
186
247
 
187
248
  // ============================================================================
@@ -196,7 +257,17 @@ export interface WishlistItem {
196
257
  product_name?: string
197
258
  product_slug?: string
198
259
  product_price?: number
199
- product_image?: string
260
+ /**
261
+ * REAL BUG FIXED (2026-09-21): was typed as an object
262
+ * (`Record<string, unknown> | null`) but the real GraphQL resolver
263
+ * (kasuvia-store-management's queries.py storefront wishlist query)
264
+ * returns a plain image URL string — a live consumer's `typeof === 'object'`
265
+ * check against this field was always false, silently hiding every
266
+ * wishlist item's image.
267
+ */
268
+ product_image?: string | null
269
+ /** Real field (queries.py's `item.product.currency`) — the SDK's own GET_STOREFRONT_WISHLIST query already fetches it. */
270
+ currency?: string
200
271
  }
201
272
 
202
273
  export interface WishlistListResponse {
@@ -216,40 +287,111 @@ export type OrderStatus =
216
287
  | 'confirmed'
217
288
  | 'processing'
218
289
  | 'packed'
219
- | 'picked_up'
290
+ | 'picked_up' // Carrier pickup (for carrier shipping)
291
+ | 'ready_for_pickup' // Customer self-pickup: order ready for customer collection
292
+ | 'picked_up_by_customer' // Customer self-pickup: customer has collected the order
220
293
  | 'in_transit'
221
294
  | 'shipped'
222
295
  | 'delivered'
223
296
  | 'voided'
224
297
  | 'refunded'
225
298
  | 'failed';
226
- export type BespokeStageKey = "received" | "development" | "finished" | "dispatched";
227
299
 
228
300
  export interface OrderItem {
229
301
  id: string;
230
- productName: string;
231
- image: string;
302
+ order_id?: string;
303
+ product_id?: string;
304
+ variant_id?: string;
305
+ product_name: string;
232
306
  quantity: number;
233
- price: number;
307
+ unit_price: number;
308
+ variant_details?: Record<string, unknown>;
309
+ custom_measurements?: Record<string, unknown>;
310
+ image_url?: string;
234
311
  }
235
312
 
236
313
  export interface Order {
237
314
  id: string;
238
- display_id?: string;
239
- items: OrderItem[];
240
- total: number;
315
+ display_id: string;
316
+ business_id: string;
317
+ customer_id?: string;
241
318
  status: OrderStatus;
242
- customerEmail: string;
243
- customerName: string;
244
- createdAt: number;
245
- updatedAt: number;
319
+ payment_status: string;
320
+ product_type?: ProductType;
321
+ subtotal_amount?: number;
322
+ discount_amount?: number;
323
+ discount_code?: string;
324
+ tax_amount?: number;
325
+ shipping_amount?: number;
326
+ total_amount: number;
327
+ currency: string;
328
+ customer_name?: string;
329
+ customer_email?: string;
330
+ carrier_name?: string;
331
+ carrier_slug?: string;
332
+ terminal_rate_id?: string;
333
+ tracking_number?: string;
334
+ delivery_status?: string;
335
+ guest_token?: string;
336
+ guest_email?: string;
337
+ guest_phone?: string;
338
+ guest_name?: string;
339
+
340
+ // Weight discrepancy & surcharge fields (Task 2)
341
+ shipping_surcharge_amount?: number;
342
+ shipping_surcharge_reason?: string;
343
+ shipping_surcharge_description?: string;
344
+ shipping_surcharge_applied_at?: string;
345
+ declared_weight_kg?: number;
346
+ actual_weight_kg?: number;
347
+
348
+ // Self-pickup verification fields (Task 3)
349
+ pickup_verification_code?: string;
350
+ pickup_verification_code_generated_at?: string;
351
+ pickup_verification_code_used_at?: string;
352
+ pickup_verification_failed_attempts?: number;
353
+ pickup_verification_locked_until?: string;
354
+ pickup_qr_payload?: string;
355
+
356
+ // Bespoke fields (Batch 4)
357
+ bespoke_brief?: string;
358
+ bespoke_reference_images?: string[];
359
+ bespoke_measurements?: Record<string, unknown>;
360
+ bespoke_preferred_timeline?: string;
361
+ bespoke_special_instructions?: string;
362
+ bespoke_deposit_ratio?: number;
363
+ bespoke_deposit_amount?: number;
364
+ bespoke_balance_amount?: number;
365
+ /** BespokeFulfillmentStatus: "processing" | "awaiting_approval" | "under_review" | "confirmed". */
366
+ bespoke_status?: string;
367
+ bespoke_deliverable_submitted_at?: string;
368
+ bespoke_deliverable_url?: string;
369
+ bespoke_deliverable_notes?: string;
370
+ bespoke_buyer_approved_at?: string;
371
+ bespoke_revision_count?: number;
372
+ bespoke_revision_notes?: string;
373
+
374
+ items: OrderItem[];
375
+ buyer_country?: string;
376
+ withdrawal_consent_given_at?: string;
377
+ created_at: string;
378
+ updated_at: string;
379
+
380
+ // Optional SDK legacy compatibility aliases for existing component consumers
381
+ total?: number;
382
+ shippingAmount?: number;
383
+ discountAmount?: number;
384
+ customerEmail?: string;
385
+ customerName?: string;
386
+ createdAt?: number;
387
+ updatedAt?: number;
246
388
  deliveryMethod?: string;
247
389
  trackingNumber?: string;
248
390
  deliveryAddress?: string;
249
391
  shippingAddress?: DeliveryAddress;
250
392
  cancellationReason?: string;
251
393
  hasBespoke?: boolean;
252
- bespokeStage?: BespokeStageKey;
394
+ bespokeStage?: string;
253
395
  bespokeBrief?: string;
254
396
  deliveryStatus?: string;
255
397
  carrierName?: string;
@@ -278,6 +420,11 @@ export interface ShippingLocation {
278
420
  lga: string;
279
421
  country: string;
280
422
  postal: string;
423
+ // Task: Plus Codes + Interactive Map Pin-Drop - Geolocation fields
424
+ latitude?: number | null;
425
+ longitude?: number | null;
426
+ plus_code?: string | null;
427
+ coordinate_source?: 'pin_drop' | 'plus_code' | 'manual' | null;
281
428
  }
282
429
 
283
430
  // ============================================================================
@@ -289,12 +436,81 @@ export interface FindOrderParams {
289
436
  order_id: string;
290
437
  }
291
438
 
439
+ /**
440
+ * Mirrors kasuvia-store-management's real PhysicalFulfillmentCreate schema
441
+ * exactly (app/schemas/order.py) — the ONLY place these fields are actually
442
+ * read from on OrderCreate. See CreateOrderParams's own doc comment for the
443
+ * real bug this replaced (they used to be declared flat on CreateOrderParams
444
+ * itself, which OrderBase's `extra: "allow"` config accepted with no
445
+ * validation error, then silently discarded — order_in.physical stayed None
446
+ * regardless of what was sent).
447
+ */
448
+ export interface CreateOrderPhysicalParams {
449
+ shipping_address?: DeliveryAddress;
450
+ shipping_latitude?: number | null;
451
+ shipping_longitude?: number | null;
452
+ shipping_plus_code?: string | null;
453
+ shipping_coordinate_source?: 'pin_drop' | 'plus_code' | 'manual' | null;
454
+ /**
455
+ * A quote_id from getShippingRates' per-rate "quote_id" field (carrier
456
+ * shipments only). Required for any nonzero shipping fee — the backend
457
+ * reads shipping_amount/terminal_rate_id/carrier_name from this stored
458
+ * quote. self_pickup orders need one too (a real zero-fee quote, see
459
+ * logisticsService.getShippingRates' self_pickup branch) — the backend
460
+ * derives resolved_location_id from it, required for both shipping_method.
461
+ */
462
+ shipping_quote_id?: string;
463
+ /** A ShippingQuoteGroup id when the cart needed splitting across multiple locations — mutually exclusive with shipping_quote_id. */
464
+ shipping_plan_id?: string;
465
+ /**
466
+ * Client-repeated for documentation only — the backend derives the REAL
467
+ * shipping method from the quote itself (resolved_terminal_rate_id is
468
+ * None for a self_pickup quote), never trusts this field. Safe to omit.
469
+ */
470
+ shipping_method?: 'carrier' | 'self_pickup';
471
+ }
472
+
473
+ /** Mirrors kasuvia-store-management's real DigitalFulfillmentCreate schema exactly. */
474
+ export interface CreateOrderDigitalParams {
475
+ /** ISO-3166-1 alpha-2 country code (e.g. "NG"). Required for digital orders — the backend's VAT lookup refuses to guess a tax jurisdiction. */
476
+ buyer_country?: string;
477
+ withdrawal_consent_given_at?: string;
478
+ }
479
+
480
+ /**
481
+ * REAL BUG FIXED (2026-09-24), live-reported ("digital checkout still does
482
+ * not work" / the [FlatRateVAT] error persisting even after buyer_country
483
+ * was correctly collected in the UI): shipping_address, shipping_quote_id,
484
+ * shipping_latitude/longitude/plus_code/coordinate_source, buyer_country,
485
+ * withdrawal_consent_given_at, and shipping_rate_id used to be declared
486
+ * FLAT on this interface. kasuvia-store-management's real OrderCreate
487
+ * schema never had flat equivalents of any of these — confirmed against
488
+ * app/schemas/order.py directly - they only ever existed nested under
489
+ * `physical`/`digital` (see CreateOrderPhysicalParams/
490
+ * CreateOrderDigitalParams above). OrderBase's `extra: "allow"` config
491
+ * meant sending them flat produced no validation error at all - Pydantic
492
+ * silently accepted and discarded them, so order_in.physical/order_in.digital
493
+ * stayed None regardless of what the checkout form collected. Every real
494
+ * physical order's shipping_address/quote and every real digital order's
495
+ * buyer_country has been silently lost this way. shipping_rate_id and
496
+ * pickup_location_id (also previously flat) never existed on the backend
497
+ * schema in ANY shape - both removed outright as confirmed-dead.
498
+ */
292
499
  export interface CreateOrderParams {
293
500
  customer_id?: string | null;
294
501
  status?: string;
295
- shipping_address?: DeliveryAddress;
296
502
  checkout_session_id?: string;
297
- items: Array<{
503
+ physical?: CreateOrderPhysicalParams;
504
+ digital?: CreateOrderDigitalParams;
505
+ /**
506
+ * Line items for the order.
507
+ *
508
+ * @important DASHBOARD-ONLY — For the merchant dashboard path (POST /sales/orders),
509
+ * these items are used directly. For storefront checkout (POST /orders/storefront),
510
+ * the backend IGNORES this field entirely and reads items from the customer's
511
+ * server-side Redis cart instead. Do NOT rely on this field for storefront orders.
512
+ */
513
+ items?: Array<{
298
514
  product_id: string;
299
515
  product_name: string;
300
516
  quantity: number;
@@ -303,16 +519,21 @@ export interface CreateOrderParams {
303
519
  variant_details?: Record<string, unknown>;
304
520
  custom_measurements?: Record<string, unknown>;
305
521
  }>;
306
- delivery_method?: string;
307
- delivery_address?: string;
308
- total?: number;
522
+ subtotal_amount?: number;
523
+ /** @deprecated Ignored by the backend when discount_code is set (recomputed server-side from the real Discount row); rejected outright if sent without a code. */
524
+ discount_amount?: number;
525
+ discount_code?: string;
526
+ promo_code?: string;
527
+ /** @deprecated Ignored by the backend — tax is always recomputed server-side from the DB VAT rate for the buyer's country. */
528
+ tax_amount?: number;
529
+ /** @deprecated Ignored by the backend for carrier shipments once physical.shipping_quote_id is set; rejected outright if sent nonzero without one. */
530
+ shipping_amount?: number;
309
531
  payment_method?: string;
310
- shipping_rate_id?: string;
311
532
  guest_token?: string;
312
533
  guest_email?: string;
313
534
  guest_phone?: string;
314
535
  guest_name?: string;
315
- product_type?: string;
536
+ product_type?: ProductType;
316
537
  bespoke_brief?: string;
317
538
  bespoke_reference_images?: string[];
318
539
  bespoke_measurements?: Record<string, string>;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Invoice Types for Kasuvia Storefront & SDK
3
+ */
4
+
5
+ export interface InvoiceLineItem {
6
+ product_name: string;
7
+ quantity: number;
8
+ unit_price: number;
9
+ total_price?: number;
10
+ variant_details?: Record<string, string | number>;
11
+ }
12
+
13
+ export interface Invoice {
14
+ id: string;
15
+ invoice_number: string;
16
+ status: "paid" | "issued" | "overdue" | "voided" | "cancelled";
17
+ subtotal_amount: number;
18
+ tax_amount: number;
19
+ discount_amount: number;
20
+ total_amount: number;
21
+ currency: string;
22
+ issued_at?: string | null;
23
+ due_date?: string | null;
24
+ paid_at?: string | null;
25
+ payment_link_url?: string | null;
26
+ customer_name?: string | null;
27
+ customer_email?: string | null;
28
+ customer_phone?: string | null;
29
+ notes_and_terms?: string | null;
30
+ line_items_snapshot?: InvoiceLineItem[];
31
+ }
@@ -5,7 +5,7 @@
5
5
  export interface ShippingRate {
6
6
  id: string;
7
7
  carrier_name: string;
8
- carrier_logo: string;
8
+ carrier_logo: string | null;
9
9
  carrier_rate_description: string;
10
10
  carrier_slug: string;
11
11
  amount: number;
@@ -17,6 +17,13 @@ export interface ShippingRate {
17
17
  pickup_available: boolean;
18
18
  dropoff_available?: boolean;
19
19
  dropoff_required?: boolean;
20
+ /**
21
+ * Persisted, single-use, ~45min-TTL quote id — pass this (not `amount`)
22
+ * as `shipping_quote_id` at order creation. The backend re-reads the
23
+ * amount/carrier from the stored quote itself; a client-submitted
24
+ * shipping amount with no valid quote_id is rejected.
25
+ */
26
+ quote_id?: string;
20
27
  }
21
28
 
22
29
  export interface ShippingRatesResponse {
@@ -32,9 +39,14 @@ export interface DeliveryAddress {
32
39
  street: string;
33
40
  city: string;
34
41
  state: string;
35
- lga: string;
42
+ lga?: string;
36
43
  country: string;
37
- postal: string;
44
+ postal?: string;
45
+ // Task: Plus Codes + Interactive Map Pin-Drop - Geolocation fields
46
+ latitude?: number | null;
47
+ longitude?: number | null;
48
+ plus_code?: string | null;
49
+ coordinate_source?: CoordinateSource;
38
50
  }
39
51
 
40
52
  // ============================================================================
@@ -47,3 +59,72 @@ export interface AddressValidationErrorData {
47
59
  cities?: Array<{ name: string; city_id: string; stateCode: string; countryCode: string }>;
48
60
  states?: Array<{ name: string; code: string; countryCode: string }>;
49
61
  }
62
+
63
+ // ============================================================================
64
+ // GEOLOCATION TYPES (Task: Plus Codes + Interactive Map Pin-Drop)
65
+ // ============================================================================
66
+
67
+ export type CoordinateSource = 'pin_drop' | 'plus_code' | 'manual' | null;
68
+
69
+ export interface GeolocationData {
70
+ latitude?: number | null;
71
+ longitude?: number | null;
72
+ plus_code?: string | null;
73
+ coordinate_source?: CoordinateSource;
74
+ }
75
+
76
+ export interface ReverseGeocodeRequest {
77
+ latitude: number;
78
+ longitude: number;
79
+ }
80
+
81
+ export interface ReverseGeocodeResponse {
82
+ city: string | null;
83
+ state: string | null;
84
+ country: string | null;
85
+ country_code: string | null;
86
+ display_name: string | null;
87
+ }
88
+
89
+ export interface PlusCodeDecodeRequest {
90
+ plus_code: string;
91
+ }
92
+
93
+ export interface PlusCodeDecodeResponse {
94
+ latitude: number;
95
+ longitude: number;
96
+ latitude_lo: number;
97
+ longitude_hi: number;
98
+ plus_code: string;
99
+ }
100
+
101
+ export interface AddressSuggestion {
102
+ place_id: string;
103
+ display_name: string;
104
+ line1: string;
105
+ city: string | null;
106
+ state: string | null;
107
+ postal_code: string | null;
108
+ country: string | null;
109
+ country_code: string | null;
110
+ latitude: number | null;
111
+ longitude: number | null;
112
+ }
113
+
114
+ export interface AddressAutocompleteResponse {
115
+ results: AddressSuggestion[];
116
+ }
117
+
118
+ export interface ShippingAddressPayload extends GeolocationData {
119
+ first_name: string;
120
+ last_name: string;
121
+ phone: string;
122
+ address_line1: string;
123
+ address_line2?: string | null;
124
+ city: string;
125
+ state: string;
126
+ country: string;
127
+ postal_code?: string | null;
128
+ is_residential?: boolean;
129
+ }
130
+
@@ -1,4 +1,4 @@
1
- export type Theme = "light" | "dark"
1
+ export type Theme = "light" | "dark" | "system"
2
2
  export type LangCode = "en" | "ha" | "yo" | "ig" | "fr"
3
3
 
4
4
  export interface AppState {