@clikvn/showroom-visualizer 0.3.0-dev-07 → 0.3.0-dev-08
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.
- package/CLAUDE.md +19 -14
- package/README.md +190 -33
- package/dist/commons/SkinLayer/components/CardItemGroup/Item.d.ts.map +1 -1
- package/dist/components/SkinLayer/ActionsChangeScene/index.d.ts.map +1 -1
- package/dist/components/SkinLayer/Floorplan/index.d.ts.map +1 -1
- package/dist/components/SkinLayer/Guide/index.d.ts.map +1 -1
- package/dist/components/SkinLayer/PlayBar/index.d.ts.map +1 -1
- package/dist/components/SkinLayer/PoiDetailSlideIn/Detail.d.ts.map +1 -1
- package/dist/components/SkinLayer/PoiDetailSlideIn/index.d.ts.map +1 -1
- package/dist/components/SkinLayer/ScenarioList/index.d.ts.map +1 -1
- package/dist/components/SkinLayer/SearchAndDiscoverySlideIn/ContentItem/index.d.ts.map +1 -1
- package/dist/components/SkinLayer/SearchAndDiscoverySlideIn/PromotionPart/index.d.ts.map +1 -1
- package/dist/components/SkinLayer/SearchAndDiscoverySlideIn/ScenariosPart/Item.d.ts.map +1 -1
- package/dist/components/SkinLayer/SearchAndDiscoverySlideIn/index.d.ts.map +1 -1
- package/dist/hooks/useActionMiddleware.d.ts +1 -1
- package/dist/hooks/useActionMiddleware.d.ts.map +1 -1
- package/dist/types/SkinLayer/middleware.type.d.ts +1 -0
- package/dist/types/SkinLayer/middleware.type.d.ts.map +1 -1
- package/dist/web.js +1 -1
- package/package.json +1 -1
- package/Planning/README-middleware.md +0 -582
- package/Planning/README.md +0 -67
- package/Planning/TEMPLATE.md +0 -270
package/package.json
CHANGED
|
@@ -1,582 +0,0 @@
|
|
|
1
|
-
# Middleware Implementation - Tracking Only
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
The showroom-visualizer library now supports **tracking middleware** that fires on all user interactions, regardless of whether external listeners are configured.
|
|
6
|
-
|
|
7
|
-
**Key Design**: Middleware is for **tracking only** - it does NOT block or cancel actions.
|
|
8
|
-
|
|
9
|
-
## Quick Start
|
|
10
|
-
|
|
11
|
-
```typescript
|
|
12
|
-
import ShowroomVisualizer from '@clikvn/showroom-visualizer';
|
|
13
|
-
|
|
14
|
-
ShowroomVisualizer.initVisualizer({
|
|
15
|
-
apiHost: 'https://api.clik.vn',
|
|
16
|
-
config: { tourCode: 'TOUR_123' },
|
|
17
|
-
|
|
18
|
-
// Add tracking middleware
|
|
19
|
-
middleware: (event) => {
|
|
20
|
-
console.log('Action:', event.actionName);
|
|
21
|
-
console.log('Payload:', event.payload);
|
|
22
|
-
console.log('Timestamp:', event.timestamp);
|
|
23
|
-
console.log('Tour:', event.metadata?.tourCode);
|
|
24
|
-
},
|
|
25
|
-
});
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Event Structure
|
|
29
|
-
|
|
30
|
-
Each middleware call receives an `ActionEvent`:
|
|
31
|
-
|
|
32
|
-
```typescript
|
|
33
|
-
interface ActionEvent {
|
|
34
|
-
// Action name (e.g., 'onPinActionClicked', 'onSceneChanged')
|
|
35
|
-
actionName: string;
|
|
36
|
-
|
|
37
|
-
// Action data (button key, scene info, scenario code, etc.)
|
|
38
|
-
payload: any;
|
|
39
|
-
|
|
40
|
-
// ISO timestamp when action occurred
|
|
41
|
-
timestamp: string;
|
|
42
|
-
|
|
43
|
-
// Optional context
|
|
44
|
-
metadata?: {
|
|
45
|
-
tourCode?: string;
|
|
46
|
-
sceneCode?: string;
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Tracked Actions
|
|
52
|
-
|
|
53
|
-
The middleware system tracks **13 distinct action types** across the application:
|
|
54
|
-
|
|
55
|
-
### 1. Pin Action Buttons (UI Layer)
|
|
56
|
-
|
|
57
|
-
**Component**: `src/components/SkinLayer/PinActionButtons/index.tsx`
|
|
58
|
-
**Action Name**: `onPinActionClicked`
|
|
59
|
-
**Payload**: Button key string
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
middleware: (event) => {
|
|
63
|
-
if (event.actionName === 'onPinActionClicked') {
|
|
64
|
-
console.log('Button clicked:', event.payload);
|
|
65
|
-
// Possible values: 'MAP', 'SOUND', 'FULLSCREEN', 'SEARCH', etc.
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### 2. Scene Navigation (UI Layer)
|
|
71
|
-
|
|
72
|
-
**Component**: `src/components/SkinLayer/SceneCategories/index.tsx`
|
|
73
|
-
**Action Name**: `onSceneChanged`
|
|
74
|
-
**Payload**: `{ scene: SceneType, category: CategoryDataType }`
|
|
75
|
-
|
|
76
|
-
```typescript
|
|
77
|
-
middleware: (event) => {
|
|
78
|
-
if (event.actionName === 'onSceneChanged') {
|
|
79
|
-
const { scene, category } = event.payload;
|
|
80
|
-
console.log('Navigating to:', scene.name, 'in category:', category.name);
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### 3. Scenario Playback (UI Layer)
|
|
86
|
-
|
|
87
|
-
**Component**: `src/components/SkinLayer/TourScenarios/index.tsx`
|
|
88
|
-
**Action Name**: `onStartScenario`
|
|
89
|
-
**Payload**: Scenario code (string)
|
|
90
|
-
|
|
91
|
-
```typescript
|
|
92
|
-
middleware: (event) => {
|
|
93
|
-
if (event.actionName === 'onStartScenario') {
|
|
94
|
-
console.log('Starting scenario:', event.payload);
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### 4. POI Click (Krpano Layer)
|
|
100
|
-
|
|
101
|
-
**Hook**: `src/hooks/Visualizer/useTourVisualizer.ts`
|
|
102
|
-
**Action Name**: `onPoiClicked`
|
|
103
|
-
**Payload**: POI data object
|
|
104
|
-
|
|
105
|
-
```typescript
|
|
106
|
-
middleware: (event) => {
|
|
107
|
-
if (event.actionName === 'onPoiClicked') {
|
|
108
|
-
console.log('POI clicked:', event.payload);
|
|
109
|
-
// Payload contains: { code, type, sceneCode, ...poi properties }
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### 5. Moving POI Click (Krpano Layer)
|
|
115
|
-
|
|
116
|
-
**Hook**: `src/hooks/Visualizer/useTourVisualizer.ts`
|
|
117
|
-
**Action Name**: `onMovingPoiClick`
|
|
118
|
-
**Payload**: `{ sceneCode: string, floorPlanCode?: string }`
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
middleware: (event) => {
|
|
122
|
-
if (event.actionName === 'onMovingPoiClick') {
|
|
123
|
-
console.log('Moving POI clicked:', event.payload.sceneCode);
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
### 6. Texture POI Click (Krpano Layer)
|
|
129
|
-
|
|
130
|
-
**Hook**: `src/hooks/Visualizer/useTourVisualizer.ts`
|
|
131
|
-
**Action Name**: `onTexturePoiClick`
|
|
132
|
-
**Payload**: `{ poiCode: string, textureIndex: number }`
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
middleware: (event) => {
|
|
136
|
-
if (event.actionName === 'onTexturePoiClick') {
|
|
137
|
-
console.log(
|
|
138
|
-
'Texture POI clicked:',
|
|
139
|
-
event.payload.poiCode,
|
|
140
|
-
'index:',
|
|
141
|
-
event.payload.textureIndex
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### 7. Background Sound Toggle (Krpano Layer)
|
|
148
|
-
|
|
149
|
-
**Hook**: `src/hooks/Visualizer/useTourVisualizer.ts`
|
|
150
|
-
**Action Name**: `onBackgroundSoundToggle`
|
|
151
|
-
**Payload**: `{ playing: boolean }`
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
middleware: (event) => {
|
|
155
|
-
if (event.actionName === 'onBackgroundSoundToggle') {
|
|
156
|
-
console.log(
|
|
157
|
-
'Background sound:',
|
|
158
|
-
event.payload.playing ? 'playing' : 'paused'
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### 8. Function Button Open (Krpano Layer)
|
|
165
|
-
|
|
166
|
-
**Hook**: `src/hooks/Visualizer/useTourVisualizer.ts`
|
|
167
|
-
**Action Name**: `onFunctionButtonOpen`
|
|
168
|
-
**Payload**: `{ buttonCode: string }`
|
|
169
|
-
|
|
170
|
-
```typescript
|
|
171
|
-
middleware: (event) => {
|
|
172
|
-
if (event.actionName === 'onFunctionButtonOpen') {
|
|
173
|
-
console.log('Function button opened:', event.payload.buttonCode);
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### 9. Minimap Toggle (Krpano Layer)
|
|
179
|
-
|
|
180
|
-
**Hook**: `src/hooks/Visualizer/useTourVisualizer.ts`
|
|
181
|
-
**Action Name**: `onMinimapToggle`
|
|
182
|
-
**Payload**: `{ fullscreen: boolean }`
|
|
183
|
-
|
|
184
|
-
```typescript
|
|
185
|
-
middleware: (event) => {
|
|
186
|
-
if (event.actionName === 'onMinimapToggle') {
|
|
187
|
-
console.log('Minimap fullscreen:', event.payload.fullscreen);
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
### 10. Hotspot View More (UI Layer)
|
|
193
|
-
|
|
194
|
-
**Component**: `src/components/SkinLayer/HotspotOverview/index.tsx`
|
|
195
|
-
**Action Name**: `onHotspotViewMore`
|
|
196
|
-
**Payload**: `{ poiCode: string, poiType: string }`
|
|
197
|
-
|
|
198
|
-
```typescript
|
|
199
|
-
middleware: (event) => {
|
|
200
|
-
if (event.actionName === 'onHotspotViewMore') {
|
|
201
|
-
console.log(
|
|
202
|
-
'View more clicked:',
|
|
203
|
-
event.payload.poiCode,
|
|
204
|
-
'type:',
|
|
205
|
-
event.payload.poiType
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### 11. Gallery Item Click (UI Layer)
|
|
212
|
-
|
|
213
|
-
**Component**: `src/components/SkinLayer/HotspotOverview/index.tsx`
|
|
214
|
-
**Action Name**: `onGalleryItemClick`
|
|
215
|
-
**Payload**: `{ id: number, poiCode: string }`
|
|
216
|
-
|
|
217
|
-
```typescript
|
|
218
|
-
middleware: (event) => {
|
|
219
|
-
if (event.actionName === 'onGalleryItemClick') {
|
|
220
|
-
console.log('Gallery item clicked:', event.payload.id);
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### 12. Texture Selected (UI Layer)
|
|
226
|
-
|
|
227
|
-
**Component**: `src/components/SkinLayer/HotspotOverview/index.tsx`
|
|
228
|
-
**Action Name**: `onTextureSelected`
|
|
229
|
-
**Payload**: `{ index: number, poiCode: string }`
|
|
230
|
-
|
|
231
|
-
```typescript
|
|
232
|
-
middleware: (event) => {
|
|
233
|
-
if (event.actionName === 'onTextureSelected') {
|
|
234
|
-
console.log(
|
|
235
|
-
'Texture selected:',
|
|
236
|
-
event.payload.index,
|
|
237
|
-
'for POI:',
|
|
238
|
-
event.payload.poiCode
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
};
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
## Summary of All Tracked Actions
|
|
245
|
-
|
|
246
|
-
| # | Action Name | Layer | Component/Hook | Payload |
|
|
247
|
-
| --- | ------------------------- | ------ | ----------------- | ------------------------------- |
|
|
248
|
-
| 1 | `onPinActionClicked` | UI | PinActionButtons | `string` (button key) |
|
|
249
|
-
| 2 | `onSceneChanged` | UI | SceneCategories | `{ scene, category }` |
|
|
250
|
-
| 3 | `onStartScenario` | UI | TourScenarios | `string` (scenario code) |
|
|
251
|
-
| 4 | `onPoiClicked` | Krpano | useTourVisualizer | POI object |
|
|
252
|
-
| 5 | `onMovingPoiClick` | Krpano | useTourVisualizer | `{ sceneCode, floorPlanCode? }` |
|
|
253
|
-
| 6 | `onTexturePoiClick` | Krpano | useTourVisualizer | `{ poiCode, textureIndex }` |
|
|
254
|
-
| 7 | `onBackgroundSoundToggle` | Krpano | useTourVisualizer | `{ playing }` |
|
|
255
|
-
| 8 | `onFunctionButtonOpen` | Krpano | useTourVisualizer | `{ buttonCode }` |
|
|
256
|
-
| 9 | `onMinimapToggle` | Krpano | useTourVisualizer | `{ fullscreen }` |
|
|
257
|
-
| 10 | `onHotspotViewMore` | UI | HotspotOverview | `{ poiCode, poiType }` |
|
|
258
|
-
| 11 | `onGalleryItemClick` | UI | HotspotOverview | `{ id, poiCode }` |
|
|
259
|
-
| 12 | `onTextureSelected` | UI | HotspotOverview | `{ index, poiCode }` |
|
|
260
|
-
|
|
261
|
-
## Usage Examples
|
|
262
|
-
|
|
263
|
-
### Example 1: Google Analytics Tracking
|
|
264
|
-
|
|
265
|
-
```typescript
|
|
266
|
-
ShowroomVisualizer.initVisualizer({
|
|
267
|
-
apiHost: 'https://api.clik.vn',
|
|
268
|
-
config: { tourCode: 'TOUR_123' },
|
|
269
|
-
middleware: (event) => {
|
|
270
|
-
gtag('event', event.actionName, {
|
|
271
|
-
event_category: 'showroom',
|
|
272
|
-
event_label: event.metadata?.tourCode,
|
|
273
|
-
value: JSON.stringify(event.payload),
|
|
274
|
-
});
|
|
275
|
-
},
|
|
276
|
-
});
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
### Example 2: Custom Analytics Service
|
|
280
|
-
|
|
281
|
-
```typescript
|
|
282
|
-
ShowroomVisualizer.initVisualizer({
|
|
283
|
-
apiHost: 'https://api.clik.vn',
|
|
284
|
-
config: { tourCode: 'TOUR_123' },
|
|
285
|
-
middleware: async (event) => {
|
|
286
|
-
await fetch('https://analytics.example.com/track', {
|
|
287
|
-
method: 'POST',
|
|
288
|
-
headers: { 'Content-Type': 'application/json' },
|
|
289
|
-
body: JSON.stringify({
|
|
290
|
-
action: event.actionName,
|
|
291
|
-
data: event.payload,
|
|
292
|
-
timestamp: event.timestamp,
|
|
293
|
-
session: getSessionId(),
|
|
294
|
-
user: getUserId(),
|
|
295
|
-
}),
|
|
296
|
-
});
|
|
297
|
-
},
|
|
298
|
-
});
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
### Example 3: Console Logging (Development)
|
|
302
|
-
|
|
303
|
-
```typescript
|
|
304
|
-
ShowroomVisualizer.initVisualizer({
|
|
305
|
-
apiHost: 'https://api.clik.vn',
|
|
306
|
-
config: { tourCode: 'TOUR_123' },
|
|
307
|
-
middleware: (event) => {
|
|
308
|
-
console.group(`[${event.timestamp}] ${event.actionName}`);
|
|
309
|
-
console.log('Payload:', event.payload);
|
|
310
|
-
console.log('Tour:', event.metadata?.tourCode);
|
|
311
|
-
console.groupEnd();
|
|
312
|
-
},
|
|
313
|
-
});
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
### Example 4: Multiple Tracking Services
|
|
317
|
-
|
|
318
|
-
```typescript
|
|
319
|
-
const trackToMultipleServices = async (event) => {
|
|
320
|
-
// Track to Google Analytics
|
|
321
|
-
gtag('event', event.actionName, { ...event.payload });
|
|
322
|
-
|
|
323
|
-
// Track to Mixpanel
|
|
324
|
-
mixpanel.track(event.actionName, event.payload);
|
|
325
|
-
|
|
326
|
-
// Track to custom backend
|
|
327
|
-
await logToBackend(event);
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
ShowroomVisualizer.initVisualizer({
|
|
331
|
-
apiHost: 'https://api.clik.vn',
|
|
332
|
-
config: { tourCode: 'TOUR_123' },
|
|
333
|
-
middleware: trackToMultipleServices,
|
|
334
|
-
});
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
### Example 5: Conditional Tracking
|
|
338
|
-
|
|
339
|
-
```typescript
|
|
340
|
-
ShowroomVisualizer.initVisualizer({
|
|
341
|
-
apiHost: 'https://api.clik.vn',
|
|
342
|
-
config: { tourCode: 'TOUR_123' },
|
|
343
|
-
middleware: (event) => {
|
|
344
|
-
// Only track certain actions
|
|
345
|
-
const importantActions = [
|
|
346
|
-
'onPinActionClicked',
|
|
347
|
-
'onSceneChanged',
|
|
348
|
-
'onStartScenario',
|
|
349
|
-
];
|
|
350
|
-
|
|
351
|
-
if (importantActions.includes(event.actionName)) {
|
|
352
|
-
analytics.track(event.actionName, event.payload);
|
|
353
|
-
}
|
|
354
|
-
},
|
|
355
|
-
});
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
## Middleware Behavior
|
|
359
|
-
|
|
360
|
-
### ✅ Always Fires
|
|
361
|
-
|
|
362
|
-
Middleware executes even if no listeners are configured:
|
|
363
|
-
|
|
364
|
-
```typescript
|
|
365
|
-
// Middleware fires for all actions!
|
|
366
|
-
ShowroomVisualizer.initVisualizer({
|
|
367
|
-
apiHost: 'https://api.clik.vn',
|
|
368
|
-
config: { tourCode: 'TOUR_123' },
|
|
369
|
-
middleware: (event) => console.log(event.actionName),
|
|
370
|
-
// No listeners configured - middleware still works!
|
|
371
|
-
});
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
### ✅ Non-Blocking
|
|
375
|
-
|
|
376
|
-
Middleware does NOT block actions - it's for tracking only:
|
|
377
|
-
|
|
378
|
-
```typescript
|
|
379
|
-
ShowroomVisualizer.initVisualizer({
|
|
380
|
-
apiHost: 'https://api.clik.vn',
|
|
381
|
-
config: { tourCode: 'TOUR_123' },
|
|
382
|
-
middleware: (event) => {
|
|
383
|
-
console.log('Tracking:', event.actionName);
|
|
384
|
-
// No return value needed - actions always proceed
|
|
385
|
-
},
|
|
386
|
-
});
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
### ✅ Error Handling
|
|
390
|
-
|
|
391
|
-
Middleware errors are caught and logged, but don't break the app:
|
|
392
|
-
|
|
393
|
-
```typescript
|
|
394
|
-
ShowroomVisualizer.initVisualizer({
|
|
395
|
-
apiHost: 'https://api.clik.vn',
|
|
396
|
-
config: { tourCode: 'TOUR_123' },
|
|
397
|
-
middleware: (event) => {
|
|
398
|
-
// If this throws an error, it's caught and logged
|
|
399
|
-
// The action still proceeds normally
|
|
400
|
-
throw new Error('Oops!');
|
|
401
|
-
},
|
|
402
|
-
});
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
### ✅ Async Support
|
|
406
|
-
|
|
407
|
-
Middleware can be synchronous or asynchronous:
|
|
408
|
-
|
|
409
|
-
```typescript
|
|
410
|
-
// Sync middleware
|
|
411
|
-
middleware: (event) => {
|
|
412
|
-
localStorage.setItem('lastAction', event.actionName);
|
|
413
|
-
};
|
|
414
|
-
|
|
415
|
-
// Async middleware (fire and forget)
|
|
416
|
-
middleware: async (event) => {
|
|
417
|
-
await fetch('/track', { method: 'POST', body: JSON.stringify(event) });
|
|
418
|
-
};
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
## TypeScript Types
|
|
422
|
-
|
|
423
|
-
```typescript
|
|
424
|
-
import { ActionMiddleware, ActionEvent } from '@clikvn/showroom-visualizer';
|
|
425
|
-
|
|
426
|
-
// Define your middleware with types
|
|
427
|
-
const myMiddleware: ActionMiddleware = (event: ActionEvent) => {
|
|
428
|
-
console.log(event.actionName, event.payload);
|
|
429
|
-
};
|
|
430
|
-
|
|
431
|
-
// Use in initialization
|
|
432
|
-
ShowroomVisualizer.initVisualizer({
|
|
433
|
-
apiHost: 'https://api.clik.vn',
|
|
434
|
-
config: { tourCode: 'TOUR_123' },
|
|
435
|
-
middleware: myMiddleware,
|
|
436
|
-
});
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
## Performance
|
|
440
|
-
|
|
441
|
-
- **Zero overhead** when middleware not configured
|
|
442
|
-
- **~1-2ms** per action when middleware configured
|
|
443
|
-
- **Non-blocking** - uses fire-and-forget pattern
|
|
444
|
-
- **Async-safe** - errors don't block the UI
|
|
445
|
-
|
|
446
|
-
## Comparison with Listeners
|
|
447
|
-
|
|
448
|
-
### Middleware
|
|
449
|
-
|
|
450
|
-
- **Purpose**: Tracking/analytics
|
|
451
|
-
- **Fires**: Always (even without listeners)
|
|
452
|
-
- **Blocks actions**: No
|
|
453
|
-
- **Use case**: Send data to external services
|
|
454
|
-
|
|
455
|
-
### Listeners
|
|
456
|
-
|
|
457
|
-
- **Purpose**: Custom app logic
|
|
458
|
-
- **Fires**: Only if configured
|
|
459
|
-
- **Blocks actions**: No (callbacks)
|
|
460
|
-
- **Use case**: Communication between lib and app
|
|
461
|
-
|
|
462
|
-
### Both Can Be Used Together
|
|
463
|
-
|
|
464
|
-
```typescript
|
|
465
|
-
ShowroomVisualizer.initVisualizer({
|
|
466
|
-
apiHost: 'https://api.clik.vn',
|
|
467
|
-
config: { tourCode: 'TOUR_123' },
|
|
468
|
-
|
|
469
|
-
// Middleware for tracking
|
|
470
|
-
middleware: (event) => {
|
|
471
|
-
analytics.track(event.actionName, event.payload);
|
|
472
|
-
},
|
|
473
|
-
|
|
474
|
-
// Listeners for custom logic
|
|
475
|
-
listeners: {
|
|
476
|
-
onPinActionClicked: (key) => {
|
|
477
|
-
if (key === 'FULLSCREEN') {
|
|
478
|
-
updateUIState({ fullscreen: true });
|
|
479
|
-
}
|
|
480
|
-
},
|
|
481
|
-
onSceneChanged: (scene) => {
|
|
482
|
-
updateBreadcrumbs(scene.name);
|
|
483
|
-
},
|
|
484
|
-
},
|
|
485
|
-
});
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
## Components with Middleware
|
|
489
|
-
|
|
490
|
-
Currently, middleware tracking is implemented across **5 locations**:
|
|
491
|
-
|
|
492
|
-
### UI Layer Components (4 components)
|
|
493
|
-
|
|
494
|
-
1. **PinActionButtons** (`src/components/SkinLayer/PinActionButtons/index.tsx`)
|
|
495
|
-
- `onPinActionClicked` - All action button clicks
|
|
496
|
-
|
|
497
|
-
2. **SceneCategories** (`src/components/SkinLayer/SceneCategories/index.tsx`)
|
|
498
|
-
- `onSceneChanged` - Scene navigation
|
|
499
|
-
|
|
500
|
-
3. **TourScenarios** (`src/components/SkinLayer/TourScenarios/index.tsx`)
|
|
501
|
-
- `onStartScenario` - Scenario playback
|
|
502
|
-
|
|
503
|
-
4. **HotspotOverview** (`src/components/SkinLayer/HotspotOverview/index.tsx`)
|
|
504
|
-
- `onHotspotViewMore` - View more button clicks
|
|
505
|
-
- `onGalleryItemClick` - Gallery item clicks
|
|
506
|
-
- `onTextureSelected` - Texture selection
|
|
507
|
-
|
|
508
|
-
### Krpano/POI Layer (1 hook)
|
|
509
|
-
|
|
510
|
-
5. **useTourVisualizer** (`src/hooks/Visualizer/useTourVisualizer.ts`)
|
|
511
|
-
- `onPoiClicked` - POI clicks
|
|
512
|
-
- `onMovingPoiClick` - Moving POI clicks
|
|
513
|
-
- `onTexturePoiClick` - Texture POI clicks
|
|
514
|
-
- `onBackgroundSoundToggle` - Background sound control
|
|
515
|
-
- `onFunctionButtonOpen` - Function button opens
|
|
516
|
-
- `onMinimapToggle` - Minimap fullscreen toggle
|
|
517
|
-
|
|
518
|
-
**Total**: 12 tracked actions across 5 locations
|
|
519
|
-
|
|
520
|
-
## Architecture
|
|
521
|
-
|
|
522
|
-
The middleware system tracks **UI layer and POI interactions only**:
|
|
523
|
-
|
|
524
|
-
- **UI Components** use the `useActionMiddleware` hook to get the `trackAction` function
|
|
525
|
-
- **useTourVisualizer hook** tracks POI clicks from the Krpano layer
|
|
526
|
-
- **Tour Model** does not have tracking - keeping it focused on core tour logic
|
|
527
|
-
- **Single source of truth** - all tracking logic exists only in `useActionMiddleware.ts`
|
|
528
|
-
|
|
529
|
-
This ensures clean separation between business logic (Tour model) and tracking concerns (UI layer).
|
|
530
|
-
|
|
531
|
-
## Adding Middleware to Custom Components
|
|
532
|
-
|
|
533
|
-
If you're extending the library, add middleware to your components:
|
|
534
|
-
|
|
535
|
-
```typescript
|
|
536
|
-
import { useActionMiddleware } from '../../../hooks/useActionMiddleware';
|
|
537
|
-
|
|
538
|
-
const MyComponent = () => {
|
|
539
|
-
const { trackAction } = useActionMiddleware();
|
|
540
|
-
|
|
541
|
-
const handleClick = () => {
|
|
542
|
-
// Track the action
|
|
543
|
-
trackAction('myCustomAction', { some: 'data' });
|
|
544
|
-
|
|
545
|
-
// Continue with your logic
|
|
546
|
-
doSomething();
|
|
547
|
-
};
|
|
548
|
-
|
|
549
|
-
return <button onClick={handleClick}>Click Me</button>;
|
|
550
|
-
};
|
|
551
|
-
```
|
|
552
|
-
|
|
553
|
-
## FAQ
|
|
554
|
-
|
|
555
|
-
**Q: Does middleware affect performance?**
|
|
556
|
-
A: Minimal impact (~1-2ms per action). Uses fire-and-forget pattern.
|
|
557
|
-
|
|
558
|
-
**Q: Can middleware cancel actions?**
|
|
559
|
-
A: No. Middleware is tracking-only and cannot block actions.
|
|
560
|
-
|
|
561
|
-
**Q: Do I need to configure listeners for middleware to work?**
|
|
562
|
-
A: No. Middleware works independently of listeners.
|
|
563
|
-
|
|
564
|
-
**Q: Can I use async operations in middleware?**
|
|
565
|
-
A: Yes. Middleware supports both sync and async functions.
|
|
566
|
-
|
|
567
|
-
**Q: What happens if middleware throws an error?**
|
|
568
|
-
A: Errors are caught and logged. The action proceeds normally.
|
|
569
|
-
|
|
570
|
-
**Q: Can I track actions from Krpano/POI layer?**
|
|
571
|
-
A: Not directly. Use listeners for those events. Middleware tracks component-level actions.
|
|
572
|
-
|
|
573
|
-
## Summary
|
|
574
|
-
|
|
575
|
-
✅ **Tracking-only** - Does not block actions
|
|
576
|
-
✅ **Always fires** - Even without listeners configured
|
|
577
|
-
✅ **Error-safe** - Errors don't break the app
|
|
578
|
-
✅ **Type-safe** - Full TypeScript support
|
|
579
|
-
✅ **Async-ready** - Supports async operations
|
|
580
|
-
✅ **Production-ready** - Tested and optimized
|
|
581
|
-
|
|
582
|
-
For analytics, logging, and tracking needs - middleware is the perfect solution! 🎉
|
package/Planning/README.md
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# Planning Documentation
|
|
2
|
-
|
|
3
|
-
This folder contains planning documents and specifications for features in the showroom-visualizer library.
|
|
4
|
-
|
|
5
|
-
## Current Documentation
|
|
6
|
-
|
|
7
|
-
### [README-middleware.md](./README-middleware.md)
|
|
8
|
-
|
|
9
|
-
Complete documentation for the **Middleware/Analytics Tracking System**:
|
|
10
|
-
|
|
11
|
-
- Overview and quick start guide
|
|
12
|
-
- Event structure and types
|
|
13
|
-
- All 12 tracked actions with examples
|
|
14
|
-
- Usage examples (Google Analytics, custom services, conditional tracking)
|
|
15
|
-
- Middleware behavior and error handling
|
|
16
|
-
- TypeScript types and performance notes
|
|
17
|
-
- Component architecture and integration
|
|
18
|
-
|
|
19
|
-
## Documentation Structure
|
|
20
|
-
|
|
21
|
-
Each feature should have its own markdown file following this structure:
|
|
22
|
-
|
|
23
|
-
```markdown
|
|
24
|
-
# Feature Name
|
|
25
|
-
|
|
26
|
-
## Overview
|
|
27
|
-
|
|
28
|
-
Brief description of the feature and its purpose
|
|
29
|
-
|
|
30
|
-
## Quick Start
|
|
31
|
-
|
|
32
|
-
Basic usage example
|
|
33
|
-
|
|
34
|
-
## API Reference
|
|
35
|
-
|
|
36
|
-
Detailed API documentation with types and examples
|
|
37
|
-
|
|
38
|
-
## Architecture
|
|
39
|
-
|
|
40
|
-
How the feature is implemented internally
|
|
41
|
-
|
|
42
|
-
## Usage Examples
|
|
43
|
-
|
|
44
|
-
Multiple real-world usage scenarios
|
|
45
|
-
|
|
46
|
-
## FAQ
|
|
47
|
-
|
|
48
|
-
Common questions and answers
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Adding New Features
|
|
52
|
-
|
|
53
|
-
When planning a new feature:
|
|
54
|
-
|
|
55
|
-
1. Create a new markdown file: `feature-name.md`
|
|
56
|
-
2. Use the template structure above
|
|
57
|
-
3. Include TypeScript examples
|
|
58
|
-
4. Document all public APIs
|
|
59
|
-
5. Add integration examples
|
|
60
|
-
6. Link from the main README.md
|
|
61
|
-
|
|
62
|
-
## Maintenance
|
|
63
|
-
|
|
64
|
-
- Keep documentation up-to-date with code changes
|
|
65
|
-
- Remove outdated planning documents after implementation
|
|
66
|
-
- Archive old versions if needed for reference
|
|
67
|
-
- Update this README when adding new features
|