@quantabit/profile-sdk 1.0.0
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/LICENSE +21 -0
- package/README.md +68 -0
- package/dist/index.cjs +1151 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.esm.js +1113 -0
- package/dist/index.esm.js.map +1 -0
- package/package.json +88 -0
- package/types/index.d.ts +84 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1151 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var sdkConfig = require('@quantabit/sdk-config');
|
|
4
|
+
var React = require('react');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Profile SDK - API 客户端
|
|
8
|
+
* 用户画像系统后端接口封装
|
|
9
|
+
*
|
|
10
|
+
* 使用 BaseApiClient 基类简化代码
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 画像 API 客户端
|
|
16
|
+
*/
|
|
17
|
+
class ProfileApiClient extends sdkConfig.BaseApiClient {
|
|
18
|
+
constructor(config = {}) {
|
|
19
|
+
super('/profile', config);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ============ 用户画像 ============
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 获取我的画像
|
|
26
|
+
*/
|
|
27
|
+
async getMyProfile() {
|
|
28
|
+
return this.get('/my');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 获取用户画像
|
|
33
|
+
* @param {string} userId - 用户 ID
|
|
34
|
+
*/
|
|
35
|
+
async getUserProfile(userId) {
|
|
36
|
+
return this.get(`/users/${userId}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 更新用户画像
|
|
41
|
+
* @param {string} userId - 用户 ID
|
|
42
|
+
* @param {Object} updates - 更新数据
|
|
43
|
+
*/
|
|
44
|
+
async updateUserProfile(userId, updates) {
|
|
45
|
+
return this.put(`/users/${userId}`, updates);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 批量获取用户画像
|
|
50
|
+
* @param {string[]} userIds - 用户 ID 列表
|
|
51
|
+
*/
|
|
52
|
+
async batchGetProfiles(userIds) {
|
|
53
|
+
return this.post('/users/batch', {
|
|
54
|
+
user_ids: userIds
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ============ 标签管理 ============
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 获取用户标签
|
|
62
|
+
* @param {string} userId - 用户 ID
|
|
63
|
+
*/
|
|
64
|
+
async getUserTags(userId) {
|
|
65
|
+
return this.get(`/users/${userId}/tags`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 获取我的标签
|
|
70
|
+
*/
|
|
71
|
+
async getMyTags() {
|
|
72
|
+
return this.get('/my/tags');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 添加用户标签
|
|
77
|
+
* @param {string} userId - 用户 ID
|
|
78
|
+
* @param {string[]} tags - 标签列表
|
|
79
|
+
*/
|
|
80
|
+
async addUserTags(userId, tags) {
|
|
81
|
+
return this.post(`/users/${userId}/tags`, {
|
|
82
|
+
tags
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 给当前用户添加标签
|
|
88
|
+
* @param {Object|string} tag - 标签对象或标签 ID
|
|
89
|
+
*/
|
|
90
|
+
async addTag(tag) {
|
|
91
|
+
return this.post('/my/tags', {
|
|
92
|
+
tag
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 移除用户标签
|
|
98
|
+
* @param {string} userId - 用户 ID
|
|
99
|
+
* @param {string[]} tags - 标签列表
|
|
100
|
+
*/
|
|
101
|
+
async removeUserTags(userId, tags) {
|
|
102
|
+
return this.delete(`/users/${userId}/tags`, {
|
|
103
|
+
tags
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 移除当前用户标签
|
|
109
|
+
* @param {string} tagId - 标签 ID
|
|
110
|
+
*/
|
|
111
|
+
async removeTag(tagId) {
|
|
112
|
+
return this.delete(`/my/tags/${tagId}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 获取所有标签
|
|
117
|
+
* @param {Object} params - 查询参数
|
|
118
|
+
*/
|
|
119
|
+
async getTags(params = {}) {
|
|
120
|
+
return this.get('/tags', params);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 创建标签
|
|
125
|
+
* @param {Object} tag - 标签数据
|
|
126
|
+
*/
|
|
127
|
+
async createTag(tag) {
|
|
128
|
+
return this.post('/tags', tag);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 获取标签统计
|
|
133
|
+
* @param {string} tagId - 标签 ID
|
|
134
|
+
*/
|
|
135
|
+
async getTagStats(tagId) {
|
|
136
|
+
return this.get(`/tags/${tagId}/stats`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ============ 人群圈选 ============
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 获取人群列表
|
|
143
|
+
* @param {Object} params - 查询参数
|
|
144
|
+
*/
|
|
145
|
+
async getSegments(params = {}) {
|
|
146
|
+
return this.get('/segments', params);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* 创建人群
|
|
151
|
+
* @param {Object} segment - 人群定义
|
|
152
|
+
*/
|
|
153
|
+
async createSegment(segment) {
|
|
154
|
+
return this.post('/segments', segment);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* 获取人群详情
|
|
159
|
+
* @param {string} segmentId - 人群 ID
|
|
160
|
+
*/
|
|
161
|
+
async getSegment(segmentId) {
|
|
162
|
+
return this.get(`/segments/${segmentId}`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* 更新人群
|
|
167
|
+
* @param {string} segmentId - 人群 ID
|
|
168
|
+
* @param {Object} updates - 更新数据
|
|
169
|
+
*/
|
|
170
|
+
async updateSegment(segmentId, updates) {
|
|
171
|
+
return this.put(`/segments/${segmentId}`, updates);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 删除人群
|
|
176
|
+
* @param {string} segmentId - 人群 ID
|
|
177
|
+
*/
|
|
178
|
+
async deleteSegment(segmentId) {
|
|
179
|
+
return this.delete(`/segments/${segmentId}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* 预估人群规模
|
|
184
|
+
* @param {Object} rules - 圈选规则
|
|
185
|
+
*/
|
|
186
|
+
async estimateSegmentSize(rules) {
|
|
187
|
+
return this.post('/segments/estimate', {
|
|
188
|
+
rules
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* 获取人群用户
|
|
194
|
+
* @param {string} segmentId - 人群 ID
|
|
195
|
+
* @param {Object} params - 查询参数
|
|
196
|
+
*/
|
|
197
|
+
async getSegmentUsers(segmentId, params = {}) {
|
|
198
|
+
return this.get(`/segments/${segmentId}/users`, params);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ============ 规则引擎 ============
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 获取可用属性
|
|
205
|
+
*/
|
|
206
|
+
async getAvailableAttributes() {
|
|
207
|
+
return this.get('/attributes');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* 获取属性值分布
|
|
212
|
+
* @param {string} attribute - 属性名
|
|
213
|
+
*/
|
|
214
|
+
async getAttributeDistribution(attribute) {
|
|
215
|
+
return this.get(`/attributes/${attribute}/distribution`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* 验证规则
|
|
220
|
+
* @param {Object} rules - 规则定义
|
|
221
|
+
*/
|
|
222
|
+
async validateRules(rules) {
|
|
223
|
+
return this.post('/rules/validate', {
|
|
224
|
+
rules
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ============ 画像分析 ============
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* 获取画像概览
|
|
232
|
+
* @param {Object} params - 查询参数
|
|
233
|
+
*/
|
|
234
|
+
async getProfileOverview(params = {}) {
|
|
235
|
+
return this.get('/analytics/overview', params);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* 获取用户分布
|
|
240
|
+
* @param {string} dimension - 维度
|
|
241
|
+
* @param {Object} params - 查询参数
|
|
242
|
+
*/
|
|
243
|
+
async getUserDistribution(dimension, params = {}) {
|
|
244
|
+
return this.get('/analytics/distribution', {
|
|
245
|
+
dimension,
|
|
246
|
+
...params
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* 获取用户趋势
|
|
252
|
+
* @param {Object} params - 查询参数
|
|
253
|
+
*/
|
|
254
|
+
async getUserTrend(params = {}) {
|
|
255
|
+
return this.get('/analytics/trend', params);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ============ 行为与偏好 ============
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* 获取我的行为画像
|
|
262
|
+
*/
|
|
263
|
+
async getMyBehavior() {
|
|
264
|
+
return this.get('/my/behavior');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* 获取用户行为画像
|
|
269
|
+
* @param {string} userId - 用户 ID
|
|
270
|
+
*/
|
|
271
|
+
async getUserBehavior(userId) {
|
|
272
|
+
return this.get(`/users/${userId}/behavior`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* 记录当前用户行为
|
|
277
|
+
* @param {string} eventType - 行为类型
|
|
278
|
+
* @param {Object} data - 行为数据
|
|
279
|
+
*/
|
|
280
|
+
async trackBehavior(eventType, data = {}) {
|
|
281
|
+
return this.post('/my/behavior', {
|
|
282
|
+
event_type: eventType,
|
|
283
|
+
data
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* 获取我的偏好设置
|
|
289
|
+
*/
|
|
290
|
+
async getPreferences() {
|
|
291
|
+
return this.get('/my/preferences');
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* 更新我的偏好设置
|
|
296
|
+
* @param {Object} preferences - 偏好设置
|
|
297
|
+
*/
|
|
298
|
+
async updatePreferences(preferences) {
|
|
299
|
+
return this.put('/my/preferences', preferences);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* 获取我的人群归属
|
|
304
|
+
*/
|
|
305
|
+
async getUserSegments() {
|
|
306
|
+
return this.get('/my/segments');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// ============ 导入导出 ============
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* 导出用户画像
|
|
313
|
+
* @param {Object} options - 导出选项
|
|
314
|
+
*/
|
|
315
|
+
async exportProfiles(options) {
|
|
316
|
+
return this.post('/export', options);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* 导入用户画像
|
|
321
|
+
* @param {Object} data - 导入数据
|
|
322
|
+
*/
|
|
323
|
+
async importProfiles(data) {
|
|
324
|
+
return this.post('/import', data);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// ============ 隐私合规 (GDPR 画像数据保护) ============
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* 导出我的画像数据 — GDPR 第20条
|
|
331
|
+
* 包括画像、标签、行为数据、偏好设置
|
|
332
|
+
* @returns {Promise<object>} 用户画像完整数据包
|
|
333
|
+
*/
|
|
334
|
+
async exportMyData() {
|
|
335
|
+
try {
|
|
336
|
+
const [profile, tags, preferences] = await Promise.allSettled([this.getMyProfile(), this.get('/my/tags'), this.get('/my/preferences')]);
|
|
337
|
+
return {
|
|
338
|
+
exportDate: new Date().toISOString(),
|
|
339
|
+
format: 'QBit Profile Export (GDPR Art. 20)',
|
|
340
|
+
profile: profile.status === 'fulfilled' ? profile.value?.data : null,
|
|
341
|
+
tags: tags.status === 'fulfilled' ? tags.value?.data : [],
|
|
342
|
+
preferences: preferences.status === 'fulfilled' ? preferences.value?.data : null
|
|
343
|
+
};
|
|
344
|
+
} catch (e) {
|
|
345
|
+
return {
|
|
346
|
+
error: e.message,
|
|
347
|
+
exportDate: new Date().toISOString()
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* 请求删除我的画像数据 — GDPR 第17条
|
|
354
|
+
* @param {string} reason - 删除原因
|
|
355
|
+
*/
|
|
356
|
+
async requestDataDeletion(reason = '') {
|
|
357
|
+
return this.post('/my/gdpr/delete', {
|
|
358
|
+
reason
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* 获取我的隐私设置
|
|
364
|
+
*/
|
|
365
|
+
async getPrivacySettings() {
|
|
366
|
+
return this.get('/my/privacy');
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* 更新我的隐私设置
|
|
371
|
+
* @param {Object} settings - 隐私偏好
|
|
372
|
+
*/
|
|
373
|
+
async updatePrivacySettings(settings) {
|
|
374
|
+
return this.put('/my/privacy', settings);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* 获取隐私数据声明
|
|
379
|
+
*/
|
|
380
|
+
getDataDisclosure() {
|
|
381
|
+
return {
|
|
382
|
+
sdk: '@quantabit/profile-sdk',
|
|
383
|
+
privacyLevel: 'analytics',
|
|
384
|
+
consentRequired: true,
|
|
385
|
+
collected: [{
|
|
386
|
+
type: 'user_profile',
|
|
387
|
+
description: 'Name, avatar, bio, preferences',
|
|
388
|
+
retention: 'Until account deletion'
|
|
389
|
+
}, {
|
|
390
|
+
type: 'user_tags',
|
|
391
|
+
description: 'Behavioral and demographic tags',
|
|
392
|
+
retention: 'Until cleared or account deletion'
|
|
393
|
+
}, {
|
|
394
|
+
type: 'behavior_data',
|
|
395
|
+
description: 'User interaction patterns for segmentation',
|
|
396
|
+
retention: '1 year (rolling)'
|
|
397
|
+
}, {
|
|
398
|
+
type: 'segments',
|
|
399
|
+
description: 'User group membership',
|
|
400
|
+
retention: 'While segment is active'
|
|
401
|
+
}],
|
|
402
|
+
legalBasis: 'Consent (profiling, Art. 6(1)(a) GDPR)',
|
|
403
|
+
compliance: ['GDPR Art. 22 (Profiling)', 'GDPR Art. 20 (Portability)'],
|
|
404
|
+
gdprCapabilities: ['export', 'delete-request', 'privacy-settings'],
|
|
405
|
+
note: 'User profiling requires explicit consent. Users can opt out via privacy settings.'
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// 创建默认实例
|
|
411
|
+
const profileApi = new ProfileApiClient();
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Profile SDK - 类型定义
|
|
415
|
+
*/
|
|
416
|
+
|
|
417
|
+
// 标签类型
|
|
418
|
+
const TagType = {
|
|
419
|
+
DEMOGRAPHIC: 'demographic',
|
|
420
|
+
// 人口属性
|
|
421
|
+
INTEREST: 'interest',
|
|
422
|
+
// 兴趣偏好
|
|
423
|
+
BEHAVIOR: 'behavior',
|
|
424
|
+
// 行为特征
|
|
425
|
+
LIFECYCLE: 'lifecycle',
|
|
426
|
+
// 生命周期
|
|
427
|
+
VALUE: 'value',
|
|
428
|
+
// 价值分层
|
|
429
|
+
CUSTOM: 'custom' // 自定义
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
// 标签来源
|
|
433
|
+
const TagSource = {
|
|
434
|
+
SYSTEM: 'system',
|
|
435
|
+
// 系统自动
|
|
436
|
+
MANUAL: 'manual',
|
|
437
|
+
// 人工打标
|
|
438
|
+
RULE: 'rule',
|
|
439
|
+
// 规则引擎
|
|
440
|
+
MODEL: 'model' // 模型预测
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
// 用户生命周期
|
|
444
|
+
const LifecycleStage = {
|
|
445
|
+
NEW: 'new',
|
|
446
|
+
ACTIVE: 'active',
|
|
447
|
+
MATURE: 'mature',
|
|
448
|
+
DECLINING: 'declining',
|
|
449
|
+
CHURNED: 'churned',
|
|
450
|
+
REVIVED: 'revived'
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
// 价值分层
|
|
454
|
+
const ValueTier = {
|
|
455
|
+
HIGH: 'high',
|
|
456
|
+
MEDIUM: 'medium',
|
|
457
|
+
LOW: 'low',
|
|
458
|
+
POTENTIAL: 'potential'
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Profile SDK - 国际化
|
|
463
|
+
*/
|
|
464
|
+
|
|
465
|
+
const SUPPORTED_LANGUAGES = ['en', 'zh', 'ja', 'ko'];
|
|
466
|
+
const messages = {
|
|
467
|
+
zh: {
|
|
468
|
+
// 画像
|
|
469
|
+
profile: '用户画像',
|
|
470
|
+
myProfile: '我的画像',
|
|
471
|
+
tags: '标签',
|
|
472
|
+
preferences: '偏好',
|
|
473
|
+
// 标签类型
|
|
474
|
+
demographic: '人口属性',
|
|
475
|
+
interest: '兴趣偏好',
|
|
476
|
+
behavior: '行为特征',
|
|
477
|
+
lifecycle: '生命周期',
|
|
478
|
+
custom: '自定义',
|
|
479
|
+
// 生命周期
|
|
480
|
+
new: '新用户',
|
|
481
|
+
active: '活跃用户',
|
|
482
|
+
loyal: '忠实用户',
|
|
483
|
+
dormant: '沉睡用户',
|
|
484
|
+
churned: '流失用户',
|
|
485
|
+
// 价值分层
|
|
486
|
+
vip: 'VIP',
|
|
487
|
+
high: '高价值',
|
|
488
|
+
medium: '中价值',
|
|
489
|
+
low: '低价值',
|
|
490
|
+
// 行为
|
|
491
|
+
lastActive: '最近活跃',
|
|
492
|
+
visitCount: '访问次数',
|
|
493
|
+
orderCount: '订单数',
|
|
494
|
+
totalSpent: '总消费',
|
|
495
|
+
avgOrderValue: '平均订单价值',
|
|
496
|
+
// 兴趣
|
|
497
|
+
categories: '偏好分类',
|
|
498
|
+
brands: '偏好品牌',
|
|
499
|
+
priceRange: '价格敏感度',
|
|
500
|
+
// 设置
|
|
501
|
+
privacySettings: '隐私设置',
|
|
502
|
+
dataCollection: '数据采集',
|
|
503
|
+
personalization: '个性化推荐',
|
|
504
|
+
enabled: '已开启',
|
|
505
|
+
disabled: '已关闭'
|
|
506
|
+
},
|
|
507
|
+
en: {
|
|
508
|
+
profile: 'User Profile',
|
|
509
|
+
myProfile: 'My Profile',
|
|
510
|
+
tags: 'Tags',
|
|
511
|
+
preferences: 'Preferences',
|
|
512
|
+
demographic: 'Demographics',
|
|
513
|
+
interest: 'Interests',
|
|
514
|
+
behavior: 'Behavior',
|
|
515
|
+
lifecycle: 'Lifecycle',
|
|
516
|
+
custom: 'Custom',
|
|
517
|
+
new: 'New',
|
|
518
|
+
active: 'Active',
|
|
519
|
+
loyal: 'Loyal',
|
|
520
|
+
dormant: 'Dormant',
|
|
521
|
+
churned: 'Churned',
|
|
522
|
+
vip: 'VIP',
|
|
523
|
+
high: 'High Value',
|
|
524
|
+
medium: 'Medium Value',
|
|
525
|
+
low: 'Low Value',
|
|
526
|
+
lastActive: 'Last Active',
|
|
527
|
+
visitCount: 'Visits',
|
|
528
|
+
orderCount: 'Orders',
|
|
529
|
+
totalSpent: 'Total Spent',
|
|
530
|
+
avgOrderValue: 'AOV',
|
|
531
|
+
categories: 'Categories',
|
|
532
|
+
brands: 'Brands',
|
|
533
|
+
priceRange: 'Price Sensitivity',
|
|
534
|
+
privacySettings: 'Privacy Settings',
|
|
535
|
+
dataCollection: 'Data Collection',
|
|
536
|
+
personalization: 'Personalization',
|
|
537
|
+
enabled: 'Enabled',
|
|
538
|
+
disabled: 'Disabled'
|
|
539
|
+
},
|
|
540
|
+
ja: {
|
|
541
|
+
profile: 'ユーザープロファイル',
|
|
542
|
+
myProfile: 'マイプロファイル',
|
|
543
|
+
tags: 'タグ',
|
|
544
|
+
preferences: '設定',
|
|
545
|
+
demographic: 'デモグラフィック',
|
|
546
|
+
interest: '興味',
|
|
547
|
+
behavior: '行動',
|
|
548
|
+
lifecycle: 'ライフサイクル',
|
|
549
|
+
custom: 'カスタム',
|
|
550
|
+
new: '新規',
|
|
551
|
+
active: 'アクティブ',
|
|
552
|
+
loyal: 'ロイヤル',
|
|
553
|
+
dormant: '休眠',
|
|
554
|
+
churned: '離脱',
|
|
555
|
+
vip: 'VIP',
|
|
556
|
+
high: '高価値',
|
|
557
|
+
medium: '中価値',
|
|
558
|
+
low: '低価値',
|
|
559
|
+
lastActive: '最終アクティブ',
|
|
560
|
+
visitCount: '訪問回数',
|
|
561
|
+
orderCount: '注文数',
|
|
562
|
+
totalSpent: '総消費額',
|
|
563
|
+
avgOrderValue: '平均注文額',
|
|
564
|
+
categories: 'カテゴリ',
|
|
565
|
+
brands: 'ブランド',
|
|
566
|
+
priceRange: '価格感度',
|
|
567
|
+
privacySettings: 'プライバシー設定',
|
|
568
|
+
dataCollection: 'データ収集',
|
|
569
|
+
personalization: 'パーソナライゼーション',
|
|
570
|
+
enabled: '有効',
|
|
571
|
+
disabled: '無効'
|
|
572
|
+
},
|
|
573
|
+
ko: {
|
|
574
|
+
profile: '사용자 프로필',
|
|
575
|
+
myProfile: '내 프로필',
|
|
576
|
+
tags: '태그',
|
|
577
|
+
preferences: '환경설정',
|
|
578
|
+
demographic: '인구통계',
|
|
579
|
+
interest: '관심사',
|
|
580
|
+
behavior: '행동',
|
|
581
|
+
lifecycle: '라이프사이클',
|
|
582
|
+
custom: '사용자 지정',
|
|
583
|
+
new: '신규',
|
|
584
|
+
active: '활성',
|
|
585
|
+
loyal: '충성',
|
|
586
|
+
dormant: '휴면',
|
|
587
|
+
churned: '이탈',
|
|
588
|
+
vip: 'VIP',
|
|
589
|
+
high: '고가치',
|
|
590
|
+
medium: '중가치',
|
|
591
|
+
low: '저가치',
|
|
592
|
+
lastActive: '최근 활동',
|
|
593
|
+
visitCount: '방문 횟수',
|
|
594
|
+
orderCount: '주문 수',
|
|
595
|
+
totalSpent: '총 지출',
|
|
596
|
+
avgOrderValue: '평균 주문 금액',
|
|
597
|
+
categories: '카테고리',
|
|
598
|
+
brands: '브랜드',
|
|
599
|
+
priceRange: '가격 민감도',
|
|
600
|
+
privacySettings: '개인정보 설정',
|
|
601
|
+
dataCollection: '데이터 수집',
|
|
602
|
+
personalization: '개인화',
|
|
603
|
+
enabled: '활성화됨',
|
|
604
|
+
disabled: '비활성화됨'
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
let currentLanguage = 'zh';
|
|
608
|
+
function setLanguage(lang) {
|
|
609
|
+
if (SUPPORTED_LANGUAGES.includes(lang)) currentLanguage = lang;
|
|
610
|
+
}
|
|
611
|
+
function getLanguage() {
|
|
612
|
+
return currentLanguage;
|
|
613
|
+
}
|
|
614
|
+
function t(key, params = {}) {
|
|
615
|
+
let text = (messages[currentLanguage] || messages.en)[key] || key;
|
|
616
|
+
Object.entries(params).forEach(([k, v]) => {
|
|
617
|
+
text = text.replace(new RegExp(`\\{${k}\\}`, 'g'), v);
|
|
618
|
+
});
|
|
619
|
+
return text;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Profile SDK - React Hooks
|
|
624
|
+
*/
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* useUserProfile - 用户画像Hook
|
|
629
|
+
*/
|
|
630
|
+
function useUserProfile(userId) {
|
|
631
|
+
const [profile, setProfile] = React.useState(null);
|
|
632
|
+
const [loading, setLoading] = React.useState(false);
|
|
633
|
+
const loadProfile = React.useCallback(async () => {
|
|
634
|
+
setLoading(true);
|
|
635
|
+
try {
|
|
636
|
+
const result = userId ? await profileApi.getUserProfile(userId) : await profileApi.getMyProfile();
|
|
637
|
+
setProfile(result);
|
|
638
|
+
} finally {
|
|
639
|
+
setLoading(false);
|
|
640
|
+
}
|
|
641
|
+
}, [userId]);
|
|
642
|
+
React.useEffect(() => {
|
|
643
|
+
loadProfile();
|
|
644
|
+
}, [userId]);
|
|
645
|
+
return {
|
|
646
|
+
profile,
|
|
647
|
+
loading,
|
|
648
|
+
refresh: loadProfile
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* useUserTags - 用户标签Hook
|
|
654
|
+
*/
|
|
655
|
+
function useUserTags(userId) {
|
|
656
|
+
const [tags, setTags] = React.useState([]);
|
|
657
|
+
const [loading, setLoading] = React.useState(false);
|
|
658
|
+
const loadTags = React.useCallback(async () => {
|
|
659
|
+
setLoading(true);
|
|
660
|
+
try {
|
|
661
|
+
const result = userId ? await profileApi.getUserTags(userId) : await profileApi.getMyTags();
|
|
662
|
+
setTags(result.tags || []);
|
|
663
|
+
} finally {
|
|
664
|
+
setLoading(false);
|
|
665
|
+
}
|
|
666
|
+
}, [userId]);
|
|
667
|
+
|
|
668
|
+
// 添加标签
|
|
669
|
+
const addTag = React.useCallback(async tag => {
|
|
670
|
+
try {
|
|
671
|
+
await profileApi.addTag(tag);
|
|
672
|
+
loadTags();
|
|
673
|
+
return true;
|
|
674
|
+
} catch (e) {
|
|
675
|
+
return false;
|
|
676
|
+
}
|
|
677
|
+
}, [loadTags]);
|
|
678
|
+
|
|
679
|
+
// 移除标签
|
|
680
|
+
const removeTag = React.useCallback(async tagId => {
|
|
681
|
+
try {
|
|
682
|
+
await profileApi.removeTag(tagId);
|
|
683
|
+
loadTags();
|
|
684
|
+
return true;
|
|
685
|
+
} catch (e) {
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
688
|
+
}, [loadTags]);
|
|
689
|
+
React.useEffect(() => {
|
|
690
|
+
loadTags();
|
|
691
|
+
}, [userId]);
|
|
692
|
+
|
|
693
|
+
// 按类型分组
|
|
694
|
+
const tagsByType = tags.reduce((acc, tag) => {
|
|
695
|
+
const type = tag.type || 'custom';
|
|
696
|
+
if (!acc[type]) acc[type] = [];
|
|
697
|
+
acc[type].push(tag);
|
|
698
|
+
return acc;
|
|
699
|
+
}, {});
|
|
700
|
+
return {
|
|
701
|
+
tags,
|
|
702
|
+
tagsByType,
|
|
703
|
+
loading,
|
|
704
|
+
addTag,
|
|
705
|
+
removeTag,
|
|
706
|
+
refresh: loadTags
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* useUserBehavior - 用户行为Hook
|
|
712
|
+
*/
|
|
713
|
+
function useUserBehavior(userId) {
|
|
714
|
+
const [behavior, setBehavior] = React.useState(null);
|
|
715
|
+
const [loading, setLoading] = React.useState(false);
|
|
716
|
+
const loadBehavior = React.useCallback(async () => {
|
|
717
|
+
setLoading(true);
|
|
718
|
+
try {
|
|
719
|
+
const result = userId ? await profileApi.getUserBehavior(userId) : await profileApi.getMyBehavior();
|
|
720
|
+
setBehavior(result);
|
|
721
|
+
} finally {
|
|
722
|
+
setLoading(false);
|
|
723
|
+
}
|
|
724
|
+
}, [userId]);
|
|
725
|
+
|
|
726
|
+
// 记录行为
|
|
727
|
+
const trackBehavior = React.useCallback(async (eventType, data) => {
|
|
728
|
+
try {
|
|
729
|
+
await profileApi.trackBehavior(eventType, data);
|
|
730
|
+
return true;
|
|
731
|
+
} catch (e) {
|
|
732
|
+
return false;
|
|
733
|
+
}
|
|
734
|
+
}, []);
|
|
735
|
+
React.useEffect(() => {
|
|
736
|
+
loadBehavior();
|
|
737
|
+
}, [userId]);
|
|
738
|
+
return {
|
|
739
|
+
behavior,
|
|
740
|
+
loading,
|
|
741
|
+
trackBehavior,
|
|
742
|
+
refresh: loadBehavior
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* useUserPreferences - 用户偏好Hook
|
|
748
|
+
*/
|
|
749
|
+
function useUserPreferences() {
|
|
750
|
+
const [preferences, setPreferences] = React.useState(null);
|
|
751
|
+
const [loading, setLoading] = React.useState(false);
|
|
752
|
+
const [saving, setSaving] = React.useState(false);
|
|
753
|
+
const loadPreferences = React.useCallback(async () => {
|
|
754
|
+
setLoading(true);
|
|
755
|
+
try {
|
|
756
|
+
const result = await profileApi.getPreferences();
|
|
757
|
+
setPreferences(result);
|
|
758
|
+
} finally {
|
|
759
|
+
setLoading(false);
|
|
760
|
+
}
|
|
761
|
+
}, []);
|
|
762
|
+
|
|
763
|
+
// 更新偏好
|
|
764
|
+
const updatePreferences = React.useCallback(async updates => {
|
|
765
|
+
setSaving(true);
|
|
766
|
+
try {
|
|
767
|
+
const result = await profileApi.updatePreferences(updates);
|
|
768
|
+
setPreferences(result);
|
|
769
|
+
return true;
|
|
770
|
+
} catch (e) {
|
|
771
|
+
return false;
|
|
772
|
+
} finally {
|
|
773
|
+
setSaving(false);
|
|
774
|
+
}
|
|
775
|
+
}, []);
|
|
776
|
+
React.useEffect(() => {
|
|
777
|
+
loadPreferences();
|
|
778
|
+
}, []);
|
|
779
|
+
return {
|
|
780
|
+
preferences,
|
|
781
|
+
loading,
|
|
782
|
+
saving,
|
|
783
|
+
updatePreferences,
|
|
784
|
+
refresh: loadPreferences
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* usePrivacySettings - 隐私设置Hook
|
|
790
|
+
*/
|
|
791
|
+
function usePrivacySettings() {
|
|
792
|
+
const [settings, setSettings] = React.useState({
|
|
793
|
+
dataCollection: true,
|
|
794
|
+
personalization: true
|
|
795
|
+
});
|
|
796
|
+
const [loading, setLoading] = React.useState(false);
|
|
797
|
+
const loadSettings = React.useCallback(async () => {
|
|
798
|
+
setLoading(true);
|
|
799
|
+
try {
|
|
800
|
+
const result = await profileApi.getPrivacySettings();
|
|
801
|
+
setSettings(result);
|
|
802
|
+
} finally {
|
|
803
|
+
setLoading(false);
|
|
804
|
+
}
|
|
805
|
+
}, []);
|
|
806
|
+
const updateSettings = React.useCallback(async (key, value) => {
|
|
807
|
+
try {
|
|
808
|
+
await profileApi.updatePrivacySettings({
|
|
809
|
+
[key]: value
|
|
810
|
+
});
|
|
811
|
+
setSettings(prev => ({
|
|
812
|
+
...prev,
|
|
813
|
+
[key]: value
|
|
814
|
+
}));
|
|
815
|
+
return true;
|
|
816
|
+
} catch (e) {
|
|
817
|
+
return false;
|
|
818
|
+
}
|
|
819
|
+
}, []);
|
|
820
|
+
React.useEffect(() => {
|
|
821
|
+
loadSettings();
|
|
822
|
+
}, []);
|
|
823
|
+
return {
|
|
824
|
+
settings,
|
|
825
|
+
loading,
|
|
826
|
+
updateSettings,
|
|
827
|
+
refresh: loadSettings
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* useUserSegments - 用户分群Hook
|
|
833
|
+
*/
|
|
834
|
+
function useUserSegments() {
|
|
835
|
+
const [segments, setSegments] = React.useState([]);
|
|
836
|
+
const [loading, setLoading] = React.useState(false);
|
|
837
|
+
const loadSegments = React.useCallback(async () => {
|
|
838
|
+
setLoading(true);
|
|
839
|
+
try {
|
|
840
|
+
const result = await profileApi.getUserSegments();
|
|
841
|
+
setSegments(result.segments || []);
|
|
842
|
+
} finally {
|
|
843
|
+
setLoading(false);
|
|
844
|
+
}
|
|
845
|
+
}, []);
|
|
846
|
+
React.useEffect(() => {
|
|
847
|
+
loadSegments();
|
|
848
|
+
}, []);
|
|
849
|
+
return {
|
|
850
|
+
segments,
|
|
851
|
+
loading,
|
|
852
|
+
refresh: loadSegments
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
/**
|
|
857
|
+
* Profile SDK - 用户画像组件
|
|
858
|
+
*/
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* 生命周期阶段颜色
|
|
863
|
+
*/
|
|
864
|
+
const lifecycleColors = {
|
|
865
|
+
[LifecycleStage.NEW]: '#3B82F6',
|
|
866
|
+
[LifecycleStage.ACTIVE]: '#10B981',
|
|
867
|
+
[LifecycleStage.LOYAL]: '#8B5CF6',
|
|
868
|
+
[LifecycleStage.DORMANT]: '#F59E0B',
|
|
869
|
+
[LifecycleStage.CHURNED]: '#EF4444'
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* 价值分层颜色
|
|
874
|
+
*/
|
|
875
|
+
const valueColors = {
|
|
876
|
+
[ValueTier.VIP]: '#EC4899',
|
|
877
|
+
[ValueTier.HIGH]: '#8B5CF6',
|
|
878
|
+
[ValueTier.MEDIUM]: '#3B82F6',
|
|
879
|
+
[ValueTier.LOW]: '#6B7280'
|
|
880
|
+
};
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* UserTag 组件
|
|
884
|
+
*/
|
|
885
|
+
function UserTag({
|
|
886
|
+
tag,
|
|
887
|
+
removable = false,
|
|
888
|
+
onRemove
|
|
889
|
+
}) {
|
|
890
|
+
const typeColors = {
|
|
891
|
+
[TagType.DEMOGRAPHIC]: '#3B82F6',
|
|
892
|
+
[TagType.INTEREST]: '#10B981',
|
|
893
|
+
[TagType.BEHAVIOR]: '#F59E0B',
|
|
894
|
+
[TagType.LIFECYCLE]: '#8B5CF6',
|
|
895
|
+
[TagType.CUSTOM]: '#6B7280'
|
|
896
|
+
};
|
|
897
|
+
const color = typeColors[tag.type] || '#6B7280';
|
|
898
|
+
return /*#__PURE__*/React.createElement("span", {
|
|
899
|
+
className: "eco-profile-tag",
|
|
900
|
+
style: {
|
|
901
|
+
backgroundColor: `${color}20`,
|
|
902
|
+
color
|
|
903
|
+
}
|
|
904
|
+
}, tag.name || tag.value, removable && onRemove && /*#__PURE__*/React.createElement("button", {
|
|
905
|
+
className: "eco-profile-tag-remove",
|
|
906
|
+
onClick: () => onRemove(tag.id)
|
|
907
|
+
}, "\xD7"));
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* TagCloud 组件
|
|
912
|
+
*/
|
|
913
|
+
function TagCloud({
|
|
914
|
+
tags = [],
|
|
915
|
+
type,
|
|
916
|
+
removable = false,
|
|
917
|
+
onRemove
|
|
918
|
+
}) {
|
|
919
|
+
const filteredTags = type ? tags.filter(t => t.type === type) : tags;
|
|
920
|
+
if (filteredTags.length === 0) {
|
|
921
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
922
|
+
className: "eco-profile-empty"
|
|
923
|
+
}, "\u6682\u65E0\u6807\u7B7E");
|
|
924
|
+
}
|
|
925
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
926
|
+
className: "eco-profile-tag-cloud"
|
|
927
|
+
}, filteredTags.map(tag => /*#__PURE__*/React.createElement(UserTag, {
|
|
928
|
+
key: tag.id,
|
|
929
|
+
tag: tag,
|
|
930
|
+
removable: removable,
|
|
931
|
+
onRemove: onRemove
|
|
932
|
+
})));
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* LifecycleIndicator 组件
|
|
937
|
+
*/
|
|
938
|
+
function LifecycleIndicator({
|
|
939
|
+
stage
|
|
940
|
+
}) {
|
|
941
|
+
const color = lifecycleColors[stage] || '#6B7280';
|
|
942
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
943
|
+
className: "eco-profile-lifecycle",
|
|
944
|
+
style: {
|
|
945
|
+
borderColor: color
|
|
946
|
+
}
|
|
947
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
948
|
+
className: "eco-profile-lifecycle-dot",
|
|
949
|
+
style: {
|
|
950
|
+
backgroundColor: color
|
|
951
|
+
}
|
|
952
|
+
}), /*#__PURE__*/React.createElement("span", {
|
|
953
|
+
className: "eco-profile-lifecycle-text"
|
|
954
|
+
}, t(stage)));
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
/**
|
|
958
|
+
* ValueTierBadge 组件
|
|
959
|
+
*/
|
|
960
|
+
function ValueTierBadge({
|
|
961
|
+
tier
|
|
962
|
+
}) {
|
|
963
|
+
const color = valueColors[tier] || '#6B7280';
|
|
964
|
+
return /*#__PURE__*/React.createElement("span", {
|
|
965
|
+
className: "eco-profile-value-badge",
|
|
966
|
+
style: {
|
|
967
|
+
backgroundColor: color
|
|
968
|
+
}
|
|
969
|
+
}, t(tier));
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
/**
|
|
973
|
+
* BehaviorStats 组件
|
|
974
|
+
*/
|
|
975
|
+
function BehaviorStats({
|
|
976
|
+
behavior
|
|
977
|
+
}) {
|
|
978
|
+
if (!behavior) return null;
|
|
979
|
+
const stats = [{
|
|
980
|
+
label: t('lastActive'),
|
|
981
|
+
value: behavior.last_active ? new Date(behavior.last_active).toLocaleDateString() : '-'
|
|
982
|
+
}, {
|
|
983
|
+
label: t('visitCount'),
|
|
984
|
+
value: behavior.visit_count || 0
|
|
985
|
+
}, {
|
|
986
|
+
label: t('orderCount'),
|
|
987
|
+
value: behavior.order_count || 0
|
|
988
|
+
}, {
|
|
989
|
+
label: t('totalSpent'),
|
|
990
|
+
value: `¥${(behavior.total_spent || 0).toLocaleString()}`
|
|
991
|
+
}, {
|
|
992
|
+
label: t('avgOrderValue'),
|
|
993
|
+
value: `¥${(behavior.avg_order_value || 0).toFixed(2)}`
|
|
994
|
+
}];
|
|
995
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
996
|
+
className: "eco-profile-behavior-stats"
|
|
997
|
+
}, stats.map((stat, i) => /*#__PURE__*/React.createElement("div", {
|
|
998
|
+
key: i,
|
|
999
|
+
className: "eco-profile-stat"
|
|
1000
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
1001
|
+
className: "eco-profile-stat-value"
|
|
1002
|
+
}, stat.value), /*#__PURE__*/React.createElement("span", {
|
|
1003
|
+
className: "eco-profile-stat-label"
|
|
1004
|
+
}, stat.label))));
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* PreferenceList 组件
|
|
1009
|
+
*/
|
|
1010
|
+
function PreferenceList({
|
|
1011
|
+
preferences,
|
|
1012
|
+
type
|
|
1013
|
+
}) {
|
|
1014
|
+
const items = preferences?.[type] || [];
|
|
1015
|
+
if (items.length === 0) {
|
|
1016
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1017
|
+
className: "eco-profile-empty"
|
|
1018
|
+
}, "\u6682\u65E0\u6570\u636E");
|
|
1019
|
+
}
|
|
1020
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1021
|
+
className: "eco-profile-preference-list"
|
|
1022
|
+
}, items.map((item, i) => /*#__PURE__*/React.createElement("div", {
|
|
1023
|
+
key: i,
|
|
1024
|
+
className: "eco-profile-preference-item"
|
|
1025
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
1026
|
+
className: "eco-profile-preference-name"
|
|
1027
|
+
}, item.name), /*#__PURE__*/React.createElement("div", {
|
|
1028
|
+
className: "eco-profile-preference-bar"
|
|
1029
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1030
|
+
className: "eco-profile-preference-fill",
|
|
1031
|
+
style: {
|
|
1032
|
+
width: `${item.score * 100}%`
|
|
1033
|
+
}
|
|
1034
|
+
})), /*#__PURE__*/React.createElement("span", {
|
|
1035
|
+
className: "eco-profile-preference-score"
|
|
1036
|
+
}, Math.round(item.score * 100), "%"))));
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* UserProfileCard 组件
|
|
1041
|
+
*/
|
|
1042
|
+
function UserProfileCard({
|
|
1043
|
+
profile,
|
|
1044
|
+
behavior,
|
|
1045
|
+
tags = [],
|
|
1046
|
+
compact = false
|
|
1047
|
+
}) {
|
|
1048
|
+
if (!profile) return null;
|
|
1049
|
+
const {
|
|
1050
|
+
lifecycle_stage,
|
|
1051
|
+
value_tier
|
|
1052
|
+
} = profile;
|
|
1053
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1054
|
+
className: `eco-profile-card ${compact ? 'compact' : ''}`
|
|
1055
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1056
|
+
className: "eco-profile-card-header"
|
|
1057
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
1058
|
+
className: "eco-profile-badges"
|
|
1059
|
+
}, lifecycle_stage && /*#__PURE__*/React.createElement(LifecycleIndicator, {
|
|
1060
|
+
stage: lifecycle_stage
|
|
1061
|
+
}), value_tier && /*#__PURE__*/React.createElement(ValueTierBadge, {
|
|
1062
|
+
tier: value_tier
|
|
1063
|
+
}))), !compact && behavior && /*#__PURE__*/React.createElement(BehaviorStats, {
|
|
1064
|
+
behavior: behavior
|
|
1065
|
+
}), tags.length > 0 && /*#__PURE__*/React.createElement("div", {
|
|
1066
|
+
className: "eco-profile-card-tags"
|
|
1067
|
+
}, /*#__PURE__*/React.createElement("h4", null, t('tags')), /*#__PURE__*/React.createElement(TagCloud, {
|
|
1068
|
+
tags: tags.slice(0, compact ? 5 : 10)
|
|
1069
|
+
})));
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/**
|
|
1073
|
+
* PrivacyToggle 组件
|
|
1074
|
+
*/
|
|
1075
|
+
function PrivacyToggle({
|
|
1076
|
+
label,
|
|
1077
|
+
value,
|
|
1078
|
+
onChange,
|
|
1079
|
+
loading = false
|
|
1080
|
+
}) {
|
|
1081
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
1082
|
+
className: "eco-profile-privacy-toggle"
|
|
1083
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
1084
|
+
className: "eco-profile-privacy-label"
|
|
1085
|
+
}, label), /*#__PURE__*/React.createElement("button", {
|
|
1086
|
+
className: `eco-profile-toggle ${value ? 'active' : ''}`,
|
|
1087
|
+
onClick: () => onChange(!value),
|
|
1088
|
+
disabled: loading
|
|
1089
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
1090
|
+
className: "eco-profile-toggle-slider"
|
|
1091
|
+
})), /*#__PURE__*/React.createElement("span", {
|
|
1092
|
+
className: "eco-profile-privacy-status"
|
|
1093
|
+
}, value ? t('enabled') : t('disabled')));
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
/**
|
|
1097
|
+
* @quantabit/profile-sdk
|
|
1098
|
+
* User Profile SDK - Full Version
|
|
1099
|
+
*/
|
|
1100
|
+
|
|
1101
|
+
const getMyProfile = () => profileApi.getMyProfile();
|
|
1102
|
+
const getUserProfile = userId => profileApi.getUserProfile(userId);
|
|
1103
|
+
const getMyTags = () => profileApi.getMyTags();
|
|
1104
|
+
const getUserTags = userId => profileApi.getUserTags(userId);
|
|
1105
|
+
const addTag = tag => profileApi.addTag(tag);
|
|
1106
|
+
const removeTag = tagId => profileApi.removeTag(tagId);
|
|
1107
|
+
const getMyBehavior = () => profileApi.getMyBehavior();
|
|
1108
|
+
const trackBehavior = (eventType, data) => profileApi.trackBehavior(eventType, data);
|
|
1109
|
+
const getPreferences = () => profileApi.getPreferences();
|
|
1110
|
+
const updatePreferences = updates => profileApi.updatePreferences(updates);
|
|
1111
|
+
const getPrivacySettings = () => profileApi.getPrivacySettings();
|
|
1112
|
+
const updatePrivacySettings = settings => profileApi.updatePrivacySettings(settings);
|
|
1113
|
+
|
|
1114
|
+
exports.BehaviorStats = BehaviorStats;
|
|
1115
|
+
exports.LifecycleIndicator = LifecycleIndicator;
|
|
1116
|
+
exports.LifecycleStage = LifecycleStage;
|
|
1117
|
+
exports.PreferenceList = PreferenceList;
|
|
1118
|
+
exports.PrivacyToggle = PrivacyToggle;
|
|
1119
|
+
exports.ProfileApiClient = ProfileApiClient;
|
|
1120
|
+
exports.SUPPORTED_LANGUAGES = SUPPORTED_LANGUAGES;
|
|
1121
|
+
exports.TagCloud = TagCloud;
|
|
1122
|
+
exports.TagSource = TagSource;
|
|
1123
|
+
exports.TagType = TagType;
|
|
1124
|
+
exports.UserProfileCard = UserProfileCard;
|
|
1125
|
+
exports.UserTag = UserTag;
|
|
1126
|
+
exports.ValueTier = ValueTier;
|
|
1127
|
+
exports.ValueTierBadge = ValueTierBadge;
|
|
1128
|
+
exports.addTag = addTag;
|
|
1129
|
+
exports.getLanguage = getLanguage;
|
|
1130
|
+
exports.getMyBehavior = getMyBehavior;
|
|
1131
|
+
exports.getMyProfile = getMyProfile;
|
|
1132
|
+
exports.getMyTags = getMyTags;
|
|
1133
|
+
exports.getPreferences = getPreferences;
|
|
1134
|
+
exports.getPrivacySettings = getPrivacySettings;
|
|
1135
|
+
exports.getUserProfile = getUserProfile;
|
|
1136
|
+
exports.getUserTags = getUserTags;
|
|
1137
|
+
exports.messages = messages;
|
|
1138
|
+
exports.profileApi = profileApi;
|
|
1139
|
+
exports.removeTag = removeTag;
|
|
1140
|
+
exports.setLanguage = setLanguage;
|
|
1141
|
+
exports.t = t;
|
|
1142
|
+
exports.trackBehavior = trackBehavior;
|
|
1143
|
+
exports.updatePreferences = updatePreferences;
|
|
1144
|
+
exports.updatePrivacySettings = updatePrivacySettings;
|
|
1145
|
+
exports.usePrivacySettings = usePrivacySettings;
|
|
1146
|
+
exports.useUserBehavior = useUserBehavior;
|
|
1147
|
+
exports.useUserPreferences = useUserPreferences;
|
|
1148
|
+
exports.useUserProfile = useUserProfile;
|
|
1149
|
+
exports.useUserSegments = useUserSegments;
|
|
1150
|
+
exports.useUserTags = useUserTags;
|
|
1151
|
+
//# sourceMappingURL=index.cjs.map
|