@gymmymac/bob-widget 3.1.19 → 3.1.20

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.
@@ -15,6 +15,12 @@ export interface Product {
15
15
  image_url?: string;
16
16
  image?: string;
17
17
  quantity?: number;
18
+ webDescription?: string;
19
+ brandDescription?: string;
20
+ perCarQty?: number;
21
+ volume?: string;
22
+ viscosity?: string;
23
+ brandImageUrl?: string;
18
24
  }
19
25
  /**
20
26
  * API response format from CARFIX retrieve-parts endpoint
@@ -77,6 +83,9 @@ export interface PreparedTier {
77
83
  isRecommended: boolean;
78
84
  isHidden: boolean;
79
85
  totalPrice: number;
86
+ originalTotalPrice?: number;
87
+ savingsAmount?: number;
88
+ bundleDiscountPercentage?: number;
80
89
  productCount: number;
81
90
  dominantBrand: string | null;
82
91
  brands: PreparedTierBrand[];
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Rear Brake Service: Disc vs Drum filter utility
3
+ * Used by mobile ServicePackageDetailView
4
+ */
5
+ export type RearBrakeType = 'disc' | 'drum';
6
+ export declare function isRearBrakePackage(pkg: {
7
+ id?: string;
8
+ title?: string;
9
+ }): boolean;
10
+ export declare function filterByBrakeType<T extends {
11
+ partslotName: string;
12
+ }>(products: T[], brakeType: RearBrakeType): T[];
13
+ export declare function recalcTierTotal<T extends {
14
+ displayPrice: number;
15
+ }>(products: T[]): number;
@@ -0,0 +1,367 @@
1
+ # Bob Widget — CARFIX Installation Brief
2
+
3
+ **Package:** `@gymmymac/bob-widget@3.1.19`
4
+ **Date:** 2026-02-13
5
+ **Status:** Production-ready, 36 unit tests passing, E2E baseline locked
6
+
7
+ ---
8
+
9
+ ## 1. Peer Dependencies (Critical for Build)
10
+
11
+ ```json
12
+ {
13
+ "peerDependencies": {
14
+ "react": "^18.0.0",
15
+ "react-dom": "^18.0.0"
16
+ },
17
+ "dependencies (bundled — no action needed)": {
18
+ "@supabase/supabase-js": "^2.84.0",
19
+ "@tanstack/react-query": "^5.0.0"
20
+ }
21
+ }
22
+ ```
23
+
24
+ > If CARFIX already uses `@tanstack/react-query`, ensure versions are compatible (v5+).
25
+
26
+ ---
27
+
28
+ ## 2. Installation
29
+
30
+ ```bash
31
+ npm install @gymmymac/bob-widget@latest
32
+ ```
33
+
34
+ ---
35
+
36
+ ## 3. Minimal Integration (4 lines)
37
+
38
+ ```tsx
39
+ import { BobStandalone } from '@gymmymac/bob-widget';
40
+
41
+ function AskBobPage() {
42
+ const router = useRouter();
43
+ const sessionToken = router.query.session as string;
44
+
45
+ return (
46
+ <div style={{
47
+ height: 'calc(100dvh - 144px - env(safe-area-inset-bottom, 0px))',
48
+ position: 'relative',
49
+ /* DO NOT use overflow: hidden — clips PTT button and chat drawer */
50
+ }}>
51
+ <BobStandalone
52
+ partner="CARFIX"
53
+ sessionToken={sessionToken}
54
+ bottomOffset={0} /* Container already handles spacing */
55
+ zIndexBase={100} /* Above header z-40 and nav z-30 */
56
+ onAddToCart={async (item) => {
57
+ // See Section 6 for full item shape including bundle metadata
58
+ await carfixCart.add(item);
59
+ }}
60
+ onNavigate={(url) => router.push(url)}
61
+ onCheckout={(checkoutUrl) => window.location.href = checkoutUrl}
62
+ onError={(error) => console.error('[Bob Error]', error)}
63
+ />
64
+ </div>
65
+ );
66
+ }
67
+ ```
68
+
69
+ > **Auto-configuration:** The `partner="CARFIX"` prop loads all API URLs, credentials, layout defaults, and feature flags from the `bob_partners` database table. No manual config needed.
70
+
71
+ ---
72
+
73
+ ## 4. CARFIX API Configuration (Already in Database)
74
+
75
+ ```json
76
+ {
77
+ "baseUrl": "https://flpzjbasdsfwoeruyxgp.supabase.co/functions/v1",
78
+ "apiKey": "eyJhbGciOiJIUzI1NiIs...wKoJ51_VPro_BrJz-A-NRpSmUW0XBP-7TJJcrhvYwxE",
79
+ "partnerCode": "CARFIX"
80
+ }
81
+ ```
82
+
83
+ ### Available Endpoints
84
+
85
+ | Endpoint | Purpose |
86
+ |---|---|
87
+ | `partner-api` | Session creation, cart, user context, orders |
88
+ | `calculate-service-bundles` | Service packs with `preparedTiers[]` (incl. bundle discounts) |
89
+ | `retrieve-vehicle-info` | NZ rego lookup |
90
+ | `retrieve-parts` | Vehicle parts by category |
91
+
92
+ ---
93
+
94
+ ## 5. Layout Constraints
95
+
96
+ ```
97
+ ┌──────────────────────────────┐
98
+ │ CARFIX Header (72px, z-40) │
99
+ ├──────────────────────────────┤
100
+ │ │
101
+ │ Bob Container │
102
+ │ height: calc(100dvh - 144px │
103
+ │ - safe-area-inset) │
104
+ │ position: relative │
105
+ │ NO overflow: hidden! │
106
+ │ │
107
+ ├──────────────────────────────┤
108
+ │ Bottom Nav (72px, z-30) │
109
+ └──────────────────────────────┘
110
+ ```
111
+
112
+ **Bob's internal z-index stack (relative to `zIndexBase={100}`):**
113
+
114
+ | Layer | z-index |
115
+ |---|---|
116
+ | Chat PTT Button | 145 |
117
+ | Chat Drawer | 130 |
118
+ | Counter Overlay | 70 |
119
+ | Bob Character | 60 |
120
+ | Product Shelf | 55 |
121
+ | Backdrop | 10 |
122
+
123
+ ---
124
+
125
+ ## 6. Callback Signatures
126
+
127
+ ### onAddToCart
128
+
129
+ ```typescript
130
+ onAddToCart: (item: {
131
+ product_id: string;
132
+ product_name: string;
133
+ quantity: number;
134
+ unit_price: number; // Final price (discount already applied for bundle items)
135
+ sku?: string;
136
+ brand?: string;
137
+ image_url?: string;
138
+ vehicle_id?: string;
139
+ // Bundle metadata (present when item is part of a service package)
140
+ is_bundle_item?: boolean;
141
+ bundle_discount_percentage?: number;
142
+ service_package_name?: string;
143
+ service_package_id?: string;
144
+ quality_tier?: string; // "Economy" | "Standard" | "Premium" | "Performance"
145
+ }) => Promise<void> | void;
146
+ ```
147
+
148
+ ### onNavigate
149
+
150
+ ```typescript
151
+ onNavigate: (url: string) => void;
152
+ // Example urls: "/product/SKU123", "/checkout"
153
+ ```
154
+
155
+ ### onCheckout
156
+
157
+ ```typescript
158
+ onCheckout: (checkoutUrl: string) => void;
159
+ // checkoutUrl is a full Stripe payment URL
160
+ ```
161
+
162
+ ### onError
163
+
164
+ ```typescript
165
+ onError: (error: Error) => void;
166
+ // Bob shows toast by default — this is for host-side logging
167
+ ```
168
+
169
+ ---
170
+
171
+ ## 7. Bundle Discount Fields (NEW)
172
+
173
+ The `calculate-service-bundles` API returns these fields per `PreparedTier`:
174
+
175
+ ```typescript
176
+ interface PreparedTier {
177
+ tierName: string; // "Economy" | "Standard" | "Premium" | "Performance"
178
+ displayName: string;
179
+ description: string;
180
+ isRecommended: boolean; // true = CARFIX Value tier
181
+ isHidden: boolean; // true = filter out (duplicate price)
182
+
183
+ // Pricing (pre-calculated by API — never recompute)
184
+ totalPrice: number; // Discounted bundle price
185
+ originalTotalPrice?: number; // Full price before discount
186
+ savingsAmount?: number; // Dollar savings (originalTotalPrice - totalPrice)
187
+ bundleDiscountPercentage?: number; // Discount % applied (0-50)
188
+
189
+ productCount: number;
190
+ dominantBrand: string | null;
191
+ brands: PreparedTierBrand[];
192
+ products: PreparedTierProduct[];
193
+ }
194
+
195
+ interface PreparedTierProduct {
196
+ partslotId: number;
197
+ partslotName: string; // "BRAKE PADS FRONT"
198
+ sku: string;
199
+ name: string;
200
+ brand: string;
201
+ brandFullName: string;
202
+ brandImageUrl: string; // Full URL — use directly in <img>
203
+ productImageUrl: string; // Full URL — use directly in <img>
204
+ price: number; // Legacy unit price
205
+ unitPrice: number; // Per-unit price
206
+ displayPrice: number; // Total (unitPrice × perCarQty) — USE THIS
207
+ isRotor: boolean; // Show "[Pair]" badge
208
+ isMultiQty: boolean; // Show quantity breakdown (e.g. spark plugs)
209
+ perCarQty: number; // Quantity needed
210
+ partNumber: string | null;
211
+ webDescription: string | null;
212
+ viscosity: string | null;
213
+ volume: number | null;
214
+ }
215
+
216
+ interface PreparedTierBrand {
217
+ name: string;
218
+ fullName: string;
219
+ imageUrl: string; // Full URL
220
+ }
221
+ ```
222
+
223
+ ### Rendering Rules
224
+
225
+ 1. **Filter hidden tiers:** `preparedTiers.filter(t => !t.isHidden)`
226
+ 2. **When `savingsAmount > 0`:** Show ~~$originalTotalPrice~~ → **$totalPrice** + "SAVE $XX — X% Bundle Deal"
227
+ 3. **When `savingsAmount === 0`:** Show `totalPrice` normally, no discount UI
228
+ 4. **Use `displayPrice`** for individual products (already includes quantity)
229
+ 5. **Never calculate prices client-side** — all values arrive pre-calculated
230
+
231
+ ---
232
+
233
+ ## 8. Session Handoff (Pre-authenticated Users)
234
+
235
+ To pass vehicle/customer context from CARFIX to Bob:
236
+
237
+ ```
238
+ 1. CARFIX calls partner-api with action: "create_session"
239
+ → Body: { vehicle_id: 42899 } ← MUST be a NUMBER
240
+ → Returns: { session_token: "abc123..." }
241
+
242
+ 2. Redirect to Bob page: /ask-bob?session=abc123...
243
+
244
+ 3. BobStandalone reads sessionToken from URL
245
+ → Calls session-handoff edge function
246
+ → Injects vehicle + customer context into chat
247
+ ```
248
+
249
+ > **Critical:** `vehicle_id` must be numeric throughout the pipeline. String values cause silent API failures.
250
+
251
+ ---
252
+
253
+ ## 9. Design Tokens
254
+
255
+ The full design token file is exported from the package:
256
+
257
+ ```typescript
258
+ import {
259
+ CARFIX_COLORS,
260
+ QUALITY_TIER_CONFIG,
261
+ IMAGE_URLS,
262
+ BADGE_CONFIG,
263
+ TYPOGRAPHY,
264
+ isRotorProduct,
265
+ getDisplayPrice,
266
+ formatNZD,
267
+ } from '@gymmymac/bob-widget';
268
+ ```
269
+
270
+ ### Key Colors
271
+
272
+ | Token | Value | Usage |
273
+ |---|---|---|
274
+ | `primary` | `#0052CC` | Standard tier, CTAs, CARFIX Value |
275
+ | `secondary` | `#38BDF8` | Accents, links |
276
+ | `accent` | `#FF8C00` | Premium tier |
277
+ | `success` | `#22C55E` | "Fits Vehicle" badges, Add to Cart |
278
+ | `destructive` | `#EF4444` | Performance tier |
279
+ | `foreground` | `#0F172A` | Headers, primary text |
280
+ | `mutedForeground` | `#64748B` | Descriptions |
281
+
282
+ ### Tier Visual Config
283
+
284
+ | Tier | Color | Badge |
285
+ |---|---|---|
286
+ | Economy | `#475569` on `#F1F5F9` | 💰 |
287
+ | Standard | `#0052CC` on `rgba(0,82,204,0.1)` | ⭐ CARFIX Value |
288
+ | Premium | `#D97706` on `#FEF3C7` | 🏆 |
289
+ | Performance | `#DC2626` on `#FEE2E2` | ⚡ |
290
+
291
+ ---
292
+
293
+ ## 10. Exported Types (Full List)
294
+
295
+ ```typescript
296
+ // Core widget components
297
+ export { BobStandalone } from '@gymmymac/bob-widget';
298
+ export type { StandaloneWidgetProps } from '@gymmymac/bob-widget';
299
+
300
+ // Types available for CARFIX integration
301
+ export type {
302
+ // Context & Config
303
+ HostContext, HostUserContext, HostVehicleContext, HostCartContext,
304
+ BobConfig, HostApiConfig, BobCallbacks, BobProviderConfig, BobLayoutConfig,
305
+
306
+ // Products & Packages
307
+ Product, CartItem, ServicePackage, PreparedTier, PreparedTierProduct, PreparedTierBrand,
308
+ Partslot, QualityTiers, Part,
309
+
310
+ // Partner
311
+ PartnerConfig, PartnerFeatureFlags, EssentialCallbacks,
312
+
313
+ // Vehicle
314
+ Vehicle,
315
+
316
+ // Messages
317
+ Message, HighlightedProduct,
318
+
319
+ // Analytics
320
+ BobAnalyticsEvent, BobGA4Config,
321
+ } from '@gymmymac/bob-widget';
322
+ ```
323
+
324
+ ---
325
+
326
+ ## 11. Verification Checklist (Post-Install)
327
+
328
+ ```
329
+ □ npm install completes without peer dependency warnings
330
+ □ BobStandalone renders loading spinner, then Bob appears
331
+ □ Bob character sits between header and bottom nav
332
+ □ Chat drawer opens above bottom navigation (z-index check)
333
+ □ PTT button is visible and not clipped
334
+ □ Vehicle lookup works (try rego: HZP550)
335
+ □ Service packages appear with tier cards
336
+ □ Bundle discount shows Was/Now pricing where applicable
337
+ □ "Add to Cart" callback fires with correct item shape
338
+ □ Session handoff works (pass ?session=TOKEN)
339
+ □ No console errors related to Bob
340
+ □ Mobile: safe-area-inset respected on notched devices
341
+ ```
342
+
343
+ ---
344
+
345
+ ## 12. Test Baseline
346
+
347
+ Bob ships with **36 unit tests** and **8+ E2E scenarios** covering:
348
+
349
+ - Callback mapping and stability
350
+ - Tier validation and empty states
351
+ - Rear Brake Disc/Drum filter logic
352
+ - Bundle discount display and cart pricing
353
+ - Vehicle lookup flow
354
+ - Service package rendering
355
+ - Chat drawer positioning
356
+
357
+ Run locally: `cd packages/bob-widget && npx vitest run`
358
+
359
+ ---
360
+
361
+ ## Support
362
+
363
+ For integration issues, the Bob team needs:
364
+ 1. Browser console output (filter for `[Bob`)
365
+ 2. Network tab showing failed API calls
366
+ 3. Screenshot of layout issue
367
+ 4. Device/browser/viewport info
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gymmymac/bob-widget",
3
- "version": "3.1.19",
3
+ "version": "3.1.20",
4
4
  "description": "Bob - AI-powered automotive parts assistant widget with multi-tenant support, RAF animations, swipeable interactions, and GA4 analytics",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",