@quantabit/risk-control-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.
@@ -0,0 +1,761 @@
1
+ import { BaseApiClient } from '@quantabit/sdk-config';
2
+ import React, { useState, useCallback, useEffect } from 'react';
3
+
4
+ /**
5
+ * Risk Control SDK - API 客户端
6
+ * 风险控制系统后端接口封装
7
+ *
8
+ * 使用 BaseApiClient 基类简化代码
9
+ */
10
+
11
+
12
+ /**
13
+ * 风控 API 客户端
14
+ */
15
+ class RiskControlApiClient extends BaseApiClient {
16
+ constructor(config = {}) {
17
+ super('/risk', config);
18
+ }
19
+
20
+ // ============ 风险评估 ============
21
+
22
+ /**
23
+ * 评估风险
24
+ * @param {Object} data - 评估数据
25
+ */
26
+ async assess(data) {
27
+ return this.post('/assess', data);
28
+ }
29
+
30
+ /**
31
+ * 检查风险(兼容快捷导出命名)
32
+ * @param {Object} data - 风险检查数据
33
+ */
34
+ async checkRisk(data) {
35
+ return this.assess(data);
36
+ }
37
+
38
+ /**
39
+ * 实时风险检测
40
+ * @param {string} eventType - 事件类型
41
+ * @param {Object} context - 上下文
42
+ */
43
+ async detect(eventType, context) {
44
+ return this.post('/detect', {
45
+ event_type: eventType,
46
+ ...context
47
+ });
48
+ }
49
+
50
+ /**
51
+ * 获取用户风险分数
52
+ * @param {string} userId - 用户 ID
53
+ */
54
+ async getUserRiskScore(userId) {
55
+ return this.get(`/users/${userId}/score`);
56
+ }
57
+
58
+ /**
59
+ * 获取用户风险画像
60
+ * @param {string} userId - 用户 ID
61
+ */
62
+ async getUserRiskProfile(userId) {
63
+ return this.get(`/users/${userId}/profile`);
64
+ }
65
+
66
+ // ============ 验证码 ============
67
+
68
+ /**
69
+ * 获取验证码
70
+ * @param {string} type - 验证码类型
71
+ * @param {Object} options - 选项
72
+ */
73
+ async getCaptcha(type, options = {}) {
74
+ return this.post('/captcha', {
75
+ type,
76
+ ...options
77
+ });
78
+ }
79
+
80
+ /**
81
+ * 验证验证码
82
+ * @param {string} captchaId - 验证码 ID
83
+ * @param {string} answer - 答案
84
+ */
85
+ async verifyCaptcha(captchaId, answer) {
86
+ return this.post('/captcha/verify', {
87
+ captcha_id: captchaId,
88
+ answer
89
+ });
90
+ }
91
+
92
+ // ============ 规则管理 ============
93
+
94
+ /**
95
+ * 获取规则列表
96
+ * @param {Object} params - 查询参数
97
+ */
98
+ async getRules(params = {}) {
99
+ return this.get('/rules', params);
100
+ }
101
+
102
+ /**
103
+ * 获取规则详情
104
+ * @param {string} ruleId - 规则 ID
105
+ */
106
+ async getRule(ruleId) {
107
+ return this.get(`/rules/${ruleId}`);
108
+ }
109
+
110
+ /**
111
+ * 创建规则
112
+ * @param {Object} rule - 规则数据
113
+ */
114
+ async createRule(rule) {
115
+ return this.post('/rules', rule);
116
+ }
117
+
118
+ /**
119
+ * 更新规则
120
+ * @param {string} ruleId - 规则 ID
121
+ * @param {Object} updates - 更新数据
122
+ */
123
+ async updateRule(ruleId, updates) {
124
+ return this.put(`/rules/${ruleId}`, updates);
125
+ }
126
+
127
+ /**
128
+ * 删除规则
129
+ * @param {string} ruleId - 规则 ID
130
+ */
131
+ async deleteRule(ruleId) {
132
+ return this.delete(`/rules/${ruleId}`);
133
+ }
134
+
135
+ /**
136
+ * 切换规则状态
137
+ * @param {string} ruleId - 规则 ID
138
+ * @param {boolean} enabled - 是否启用
139
+ */
140
+ async toggleRule(ruleId, enabled) {
141
+ return this.post(`/rules/${ruleId}/toggle`, {
142
+ enabled
143
+ });
144
+ }
145
+
146
+ // ============ 事件上报 ============
147
+
148
+ /**
149
+ * 上报风险事件
150
+ * @param {Object} event - 事件数据
151
+ */
152
+ async reportEvent(event) {
153
+ return this.post('/events', event);
154
+ }
155
+
156
+ /**
157
+ * 批量上报事件
158
+ * @param {Object[]} events - 事件列表
159
+ */
160
+ async batchReportEvents(events) {
161
+ return this.post('/events/batch', {
162
+ events
163
+ });
164
+ }
165
+
166
+ /**
167
+ * 获取事件列表
168
+ * @param {Object} params - 查询参数
169
+ */
170
+ async getEvents(params = {}) {
171
+ return this.get('/events', params);
172
+ }
173
+
174
+ // ============ 处置操作 ============
175
+
176
+ /**
177
+ * 封禁用户
178
+ * @param {string} userId - 用户 ID
179
+ * @param {Object} options - 封禁选项
180
+ */
181
+ async banUser(userId, options = {}) {
182
+ return this.post(`/users/${userId}/ban`, options);
183
+ }
184
+
185
+ /**
186
+ * 解封用户
187
+ * @param {string} userId - 用户 ID
188
+ */
189
+ async unbanUser(userId) {
190
+ return this.post(`/users/${userId}/unban`);
191
+ }
192
+
193
+ /**
194
+ * 限制用户
195
+ * @param {string} userId - 用户 ID
196
+ * @param {Object} restrictions - 限制配置
197
+ */
198
+ async restrictUser(userId, restrictions) {
199
+ return this.post(`/users/${userId}/restrict`, restrictions);
200
+ }
201
+
202
+ // ============ 统计分析 ============
203
+
204
+ /**
205
+ * 获取风控统计
206
+ * @param {Object} params - 统计参数
207
+ */
208
+ async getStats(params = {}) {
209
+ return this.get('/stats', params);
210
+ }
211
+
212
+ /**
213
+ * 获取风险趋势
214
+ * @param {Object} params - 查询参数
215
+ */
216
+ async getTrend(params = {}) {
217
+ return this.get('/trend', params);
218
+ }
219
+
220
+ /**
221
+ * 获取告警列表
222
+ * @param {Object} params - 查询参数
223
+ */
224
+ async getAlerts(params = {}) {
225
+ return this.get('/alerts', params);
226
+ }
227
+
228
+ /**
229
+ * 处理告警
230
+ * @param {string} alertId - 告警 ID
231
+ * @param {Object} action - 处理动作
232
+ */
233
+ async handleAlert(alertId, action) {
234
+ return this.post(`/alerts/${alertId}/handle`, action);
235
+ }
236
+
237
+ // ============ 隐私合规 ============
238
+
239
+ /**
240
+ * 获取用户风控数据概要 — GDPR 第15条: 访问权
241
+ * 用户有权知道被收集的风控信息
242
+ * @param {string} userId - 用户 ID
243
+ * @returns {Promise<object>} 风控数据概要(不含算法细节)
244
+ */
245
+ async getUserDataSummary(userId) {
246
+ return this.get(`/users/${userId}/data-summary`);
247
+ }
248
+
249
+ /**
250
+ * 获取隐私数据声明
251
+ */
252
+ getDataDisclosure() {
253
+ return {
254
+ sdk: '@quantabit/risk-control-sdk',
255
+ privacyLevel: 'essential',
256
+ consentRequired: false,
257
+ collected: [{
258
+ type: 'risk_score',
259
+ description: 'User risk assessment score',
260
+ retention: '2 years'
261
+ }, {
262
+ type: 'behavior_events',
263
+ description: 'Security-relevant user actions',
264
+ retention: '1 year'
265
+ }, {
266
+ type: 'device_fingerprint',
267
+ description: 'Device identification for fraud detection',
268
+ retention: '90 days'
269
+ }, {
270
+ type: 'ip_address',
271
+ description: 'IP for geo-risk analysis (anonymized after 30 days)',
272
+ retention: '30 days (full), 1 year (anonymized)'
273
+ }, {
274
+ type: 'captcha_responses',
275
+ description: 'Bot detection verification',
276
+ retention: '24 hours'
277
+ }],
278
+ legalBasis: 'Legitimate interest (fraud prevention, Art. 6(1)(f) GDPR)',
279
+ compliance: ['GDPR Art. 22 (Automated decision-making)', 'PSD2 SCA'],
280
+ gdprCapabilities: ['access-summary'],
281
+ note: 'Risk scoring uses automated processing. Users have the right to contest automated decisions under GDPR Art. 22.'
282
+ };
283
+ }
284
+ }
285
+
286
+ // 创建默认实例
287
+ const riskControlApi = new RiskControlApiClient();
288
+
289
+ /**
290
+ * Risk Control SDK - 类型定义
291
+ */
292
+
293
+ // 风险等级
294
+ const RiskLevel = {
295
+ LOW: 'low',
296
+ MEDIUM: 'medium',
297
+ HIGH: 'high',
298
+ CRITICAL: 'critical'
299
+ };
300
+
301
+ // 事件类型
302
+ const RiskEventType = {
303
+ LOGIN: 'login',
304
+ REGISTER: 'register',
305
+ PAYMENT: 'payment',
306
+ WITHDRAW: 'withdraw',
307
+ CONTENT: 'content',
308
+ INTERACTION: 'interaction'
309
+ };
310
+ const RuleCondition = {
311
+ EQUALS: 'equals',
312
+ NOT_EQUALS: 'not_equals',
313
+ GREATER_THAN: 'greater_than',
314
+ LESS_THAN: 'less_than',
315
+ CONTAINS: 'contains',
316
+ IN: 'in'
317
+ };
318
+ const EventType = RiskEventType;
319
+ const RiskAction = {
320
+ PASS: 'pass',
321
+ REVIEW: 'review',
322
+ BLOCK: 'block',
323
+ CAPTCHA: 'captcha',
324
+ LIMIT: 'limit'
325
+ };
326
+
327
+ /**
328
+ * Risk Control SDK - 多语言支持
329
+ */
330
+
331
+ const messages = {
332
+ en: {
333
+ riskControl: 'Risk Control',
334
+ riskRules: 'Risk Rules',
335
+ riskEvents: 'Risk Events',
336
+ antiCheat: 'Anti-Cheat',
337
+ createRule: 'Create Rule',
338
+ editRule: 'Edit Rule',
339
+ ruleName: 'Rule Name',
340
+ ruleDescription: 'Description',
341
+ conditions: 'Conditions',
342
+ actions: 'Actions',
343
+ priority: 'Priority',
344
+ enabled: 'Enabled',
345
+ disabled: 'Disabled',
346
+ riskLevel: 'Risk Level',
347
+ high: 'High',
348
+ medium: 'Medium',
349
+ low: 'Low',
350
+ block: 'Block',
351
+ warn: 'Warn',
352
+ review: 'Review',
353
+ pass: 'Pass',
354
+ challenged: 'Challenged',
355
+ blocked: 'Blocked',
356
+ eventType: 'Event Type',
357
+ login: 'Login',
358
+ payment: 'Payment',
359
+ withdraw: 'Withdraw',
360
+ register: 'Register',
361
+ timestamp: 'Timestamp',
362
+ userId: 'User ID',
363
+ ipAddress: 'IP Address',
364
+ deviceId: 'Device ID',
365
+ riskScore: 'Risk Score',
366
+ triggeredRules: 'Triggered Rules',
367
+ details: 'Details',
368
+ loading: 'Loading...',
369
+ noData: 'No data',
370
+ save: 'Save',
371
+ cancel: 'Cancel'
372
+ },
373
+ zh: {
374
+ riskControl: '风控',
375
+ riskRules: '风控规则',
376
+ riskEvents: '风险事件',
377
+ antiCheat: '反作弊',
378
+ createRule: '创建规则',
379
+ editRule: '编辑规则',
380
+ ruleName: '规则名称',
381
+ ruleDescription: '描述',
382
+ conditions: '条件',
383
+ actions: '动作',
384
+ priority: '优先级',
385
+ enabled: '启用',
386
+ disabled: '禁用',
387
+ riskLevel: '风险等级',
388
+ high: '高',
389
+ medium: '中',
390
+ low: '低',
391
+ block: '拦截',
392
+ warn: '警告',
393
+ review: '人工审核',
394
+ pass: '通过',
395
+ challenged: '挑战',
396
+ blocked: '已拦截',
397
+ eventType: '事件类型',
398
+ login: '登录',
399
+ payment: '支付',
400
+ withdraw: '提现',
401
+ register: '注册',
402
+ timestamp: '时间',
403
+ userId: '用户ID',
404
+ ipAddress: 'IP地址',
405
+ deviceId: '设备ID',
406
+ riskScore: '风险分数',
407
+ triggeredRules: '触发规则',
408
+ details: '详情',
409
+ loading: '加载中...',
410
+ noData: '暂无数据',
411
+ save: '保存',
412
+ cancel: '取消'
413
+ },
414
+ ja: {
415
+ riskControl: 'リスク管理',
416
+ riskRules: 'リスクルール',
417
+ riskEvents: 'リスクイベント',
418
+ antiCheat: 'アンチチート',
419
+ createRule: 'ルールの作成',
420
+ editRule: 'ルールの編集',
421
+ ruleName: 'ルール名',
422
+ ruleDescription: '説明',
423
+ conditions: '条件',
424
+ actions: 'アクション',
425
+ priority: '優先度',
426
+ enabled: '有効',
427
+ disabled: '無効',
428
+ riskLevel: 'リスクレベル',
429
+ high: '高',
430
+ medium: '中',
431
+ low: '低',
432
+ block: 'ブロック',
433
+ warn: '警告',
434
+ review: 'レビュー',
435
+ pass: 'パス',
436
+ challenged: 'チャレンジ済み',
437
+ blocked: 'ブロック済み',
438
+ eventType: 'イベントタイプ',
439
+ login: 'ログイン',
440
+ payment: '支払い',
441
+ withdraw: '出金',
442
+ register: '登録',
443
+ timestamp: 'タイムスタンプ',
444
+ userId: 'ユーザーID',
445
+ ipAddress: 'IPアドレス',
446
+ deviceId: 'デバイスID',
447
+ riskScore: 'リスクスコア',
448
+ triggeredRules: 'トリガーされたルール',
449
+ details: '詳細',
450
+ loading: '読み込み中...',
451
+ noData: 'データなし',
452
+ save: '保存',
453
+ cancel: 'キャンセル'
454
+ },
455
+ ko: {
456
+ riskControl: '리스크 관리',
457
+ riskRules: '리스크 규칙',
458
+ riskEvents: '리스크 이벤트',
459
+ antiCheat: '안티 치트',
460
+ createRule: '규칙 생성',
461
+ editRule: '규칙 편집',
462
+ ruleName: '규칙 이름',
463
+ ruleDescription: '설명',
464
+ conditions: '조건',
465
+ actions: '작업',
466
+ priority: '우선순위',
467
+ enabled: '활성화됨',
468
+ disabled: '비활성화됨',
469
+ riskLevel: '리스크 수준',
470
+ high: '높음',
471
+ medium: '중간',
472
+ low: '낮음',
473
+ block: '차단',
474
+ warn: '경고',
475
+ review: '검토',
476
+ pass: '통과',
477
+ challenged: '도전됨',
478
+ blocked: '차단됨',
479
+ eventType: '이벤트 유형',
480
+ login: '로그인',
481
+ payment: '결제',
482
+ withdraw: '출금',
483
+ register: '등록',
484
+ timestamp: '타임스탬프',
485
+ userId: '사용자 ID',
486
+ ipAddress: 'IP 주소',
487
+ deviceId: '디바이스 ID',
488
+ riskScore: '리스크 점수',
489
+ triggeredRules: '트리거된 규칙',
490
+ details: '세부 정보',
491
+ loading: '로딩 중...',
492
+ noData: '데이터 없음',
493
+ save: '저장',
494
+ cancel: '취소'
495
+ }
496
+ };
497
+ const SUPPORTED_LANGUAGES = ['en', 'zh', 'ja', 'ko'];
498
+ let currentLanguage = 'zh';
499
+ function setLanguage(lang) {
500
+ if (SUPPORTED_LANGUAGES.includes(lang)) currentLanguage = lang;
501
+ }
502
+ function getLanguage() {
503
+ return currentLanguage;
504
+ }
505
+ function t(key) {
506
+ return (messages[currentLanguage] || messages.en)[key] || key;
507
+ }
508
+
509
+ /**
510
+ * Risk Control SDK - React Hooks
511
+ */
512
+
513
+ function useRiskRules(options = {}) {
514
+ const [rules, setRules] = useState([]);
515
+ const [loading, setLoading] = useState(true);
516
+ const [error, setError] = useState(null);
517
+ const fetch = useCallback(async () => {
518
+ setLoading(true);
519
+ try {
520
+ const result = await riskControlApi.getRules(options);
521
+ setRules(result.items || result);
522
+ setError(null);
523
+ } catch (err) {
524
+ setError(err);
525
+ } finally {
526
+ setLoading(false);
527
+ }
528
+ }, [JSON.stringify(options)]);
529
+ useEffect(() => {
530
+ fetch();
531
+ }, [fetch]);
532
+ return {
533
+ rules,
534
+ loading,
535
+ error,
536
+ refresh: fetch
537
+ };
538
+ }
539
+ function useRiskEvents(options = {}) {
540
+ const [events, setEvents] = useState([]);
541
+ const [loading, setLoading] = useState(true);
542
+ const [error, setError] = useState(null);
543
+ const fetch = useCallback(async () => {
544
+ setLoading(true);
545
+ try {
546
+ const result = await riskControlApi.getEvents(options);
547
+ setEvents(result.items || result);
548
+ setError(null);
549
+ } catch (err) {
550
+ setError(err);
551
+ } finally {
552
+ setLoading(false);
553
+ }
554
+ }, [JSON.stringify(options)]);
555
+ useEffect(() => {
556
+ fetch();
557
+ }, [fetch]);
558
+ return {
559
+ events,
560
+ loading,
561
+ error,
562
+ refresh: fetch
563
+ };
564
+ }
565
+ function useRiskCheck() {
566
+ const [loading, setLoading] = useState(false);
567
+ const [result, setResult] = useState(null);
568
+ const checkRisk = useCallback(async data => {
569
+ setLoading(true);
570
+ try {
571
+ const res = await riskControlApi.checkRisk(data);
572
+ setResult(res);
573
+ return res;
574
+ } catch (err) {
575
+ setResult({
576
+ error: err.message
577
+ });
578
+ throw err;
579
+ } finally {
580
+ setLoading(false);
581
+ }
582
+ }, []);
583
+ return {
584
+ checkRisk,
585
+ result,
586
+ loading
587
+ };
588
+ }
589
+
590
+ /**
591
+ * Risk Control SDK - 风控组件
592
+ */
593
+
594
+ function RiskLevelBadge({
595
+ level
596
+ }) {
597
+ const levelMap = {
598
+ high: 'risk-badge-high',
599
+ medium: 'risk-badge-medium',
600
+ low: 'risk-badge-low'
601
+ };
602
+ return /*#__PURE__*/React.createElement("span", {
603
+ className: `risk-badge ${levelMap[level] || levelMap.low}`
604
+ }, t(level));
605
+ }
606
+ function RiskScore({
607
+ score
608
+ }) {
609
+ const level = score >= 70 ? 'high' : score >= 40 ? 'medium' : 'low';
610
+ return /*#__PURE__*/React.createElement("div", {
611
+ className: `risk-score risk-score-${level}`
612
+ }, score);
613
+ }
614
+ function RiskEventTable({
615
+ events = [],
616
+ loading = false,
617
+ onViewDetails,
618
+ className = ''
619
+ }) {
620
+ if (loading) {
621
+ return /*#__PURE__*/React.createElement("div", {
622
+ className: `risk-card ${className}`
623
+ }, /*#__PURE__*/React.createElement("div", {
624
+ className: "risk-loading"
625
+ }, /*#__PURE__*/React.createElement("div", {
626
+ className: "risk-spinner"
627
+ })));
628
+ }
629
+ if (events.length === 0) {
630
+ return /*#__PURE__*/React.createElement("div", {
631
+ className: `risk-card ${className}`
632
+ }, /*#__PURE__*/React.createElement("div", {
633
+ className: "risk-empty"
634
+ }, /*#__PURE__*/React.createElement("div", {
635
+ style: {
636
+ fontSize: '48px',
637
+ marginBottom: '16px'
638
+ }
639
+ }, "\uD83D\uDEE1\uFE0F"), /*#__PURE__*/React.createElement("div", null, t('noData'))));
640
+ }
641
+ return /*#__PURE__*/React.createElement("div", {
642
+ className: `risk-card ${className}`
643
+ }, /*#__PURE__*/React.createElement("table", {
644
+ className: "risk-table"
645
+ }, /*#__PURE__*/React.createElement("thead", null, /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("th", null, t('timestamp')), /*#__PURE__*/React.createElement("th", null, t('eventType')), /*#__PURE__*/React.createElement("th", null, t('userId')), /*#__PURE__*/React.createElement("th", null, t('riskScore')), /*#__PURE__*/React.createElement("th", null, t('riskLevel')), /*#__PURE__*/React.createElement("th", null))), /*#__PURE__*/React.createElement("tbody", null, events.map(event => /*#__PURE__*/React.createElement("tr", {
646
+ key: event.id
647
+ }, /*#__PURE__*/React.createElement("td", null, new Date(event.timestamp).toLocaleString()), /*#__PURE__*/React.createElement("td", null, t(event.event_type) || event.event_type), /*#__PURE__*/React.createElement("td", null, /*#__PURE__*/React.createElement("code", {
648
+ style: {
649
+ background: '#f3f4f6',
650
+ padding: '2px 6px',
651
+ borderRadius: '4px'
652
+ }
653
+ }, event.user_id)), /*#__PURE__*/React.createElement("td", null, /*#__PURE__*/React.createElement(RiskScore, {
654
+ score: event.risk_score
655
+ })), /*#__PURE__*/React.createElement("td", null, /*#__PURE__*/React.createElement(RiskLevelBadge, {
656
+ level: event.risk_level
657
+ })), /*#__PURE__*/React.createElement("td", null, onViewDetails && /*#__PURE__*/React.createElement("button", {
658
+ className: "risk-btn risk-btn-outline",
659
+ style: {
660
+ padding: '4px 10px',
661
+ fontSize: '12px'
662
+ },
663
+ onClick: () => onViewDetails(event)
664
+ }, t('details'))))))));
665
+ }
666
+ function RiskRuleCard({
667
+ rule,
668
+ onEdit,
669
+ onToggle
670
+ }) {
671
+ return /*#__PURE__*/React.createElement("div", {
672
+ className: "risk-rule-item"
673
+ }, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
674
+ className: "risk-rule-name"
675
+ }, rule.name), /*#__PURE__*/React.createElement("div", {
676
+ className: "risk-rule-desc"
677
+ }, rule.description)), /*#__PURE__*/React.createElement("div", {
678
+ style: {
679
+ display: 'flex',
680
+ alignItems: 'center',
681
+ gap: '12px'
682
+ }
683
+ }, /*#__PURE__*/React.createElement(RiskLevelBadge, {
684
+ level: rule.risk_level
685
+ }), /*#__PURE__*/React.createElement("label", {
686
+ style: {
687
+ display: 'flex',
688
+ alignItems: 'center',
689
+ gap: '8px',
690
+ cursor: 'pointer'
691
+ }
692
+ }, /*#__PURE__*/React.createElement("input", {
693
+ type: "checkbox",
694
+ checked: rule.enabled,
695
+ onChange: () => onToggle?.(rule.id, !rule.enabled),
696
+ style: {
697
+ width: '18px',
698
+ height: '18px'
699
+ }
700
+ }), /*#__PURE__*/React.createElement("span", {
701
+ style: {
702
+ fontSize: '13px',
703
+ color: '#6b7280'
704
+ }
705
+ }, rule.enabled ? t('enabled') : t('disabled'))), onEdit && /*#__PURE__*/React.createElement("button", {
706
+ className: "risk-btn risk-btn-outline",
707
+ style: {
708
+ padding: '6px 12px',
709
+ fontSize: '12px'
710
+ },
711
+ onClick: () => onEdit(rule)
712
+ }, t('editRule'))));
713
+ }
714
+ function RiskRulesList({
715
+ rules = [],
716
+ loading = false,
717
+ onEdit,
718
+ onToggle,
719
+ className = ''
720
+ }) {
721
+ if (loading) {
722
+ return /*#__PURE__*/React.createElement("div", {
723
+ className: `risk-card ${className}`
724
+ }, /*#__PURE__*/React.createElement("div", {
725
+ className: "risk-loading"
726
+ }, /*#__PURE__*/React.createElement("div", {
727
+ className: "risk-spinner"
728
+ })));
729
+ }
730
+ if (rules.length === 0) {
731
+ return /*#__PURE__*/React.createElement("div", {
732
+ className: `risk-card ${className}`
733
+ }, /*#__PURE__*/React.createElement("div", {
734
+ className: "risk-empty"
735
+ }, t('noData')));
736
+ }
737
+ return /*#__PURE__*/React.createElement("div", {
738
+ className: `risk-card ${className}`
739
+ }, /*#__PURE__*/React.createElement("div", {
740
+ className: "risk-rules-list"
741
+ }, rules.map(rule => /*#__PURE__*/React.createElement(RiskRuleCard, {
742
+ key: rule.id,
743
+ rule: rule,
744
+ onEdit: onEdit,
745
+ onToggle: onToggle
746
+ }))));
747
+ }
748
+
749
+ /**
750
+ * @quantabit/risk-control-sdk
751
+ * Risk Control SDK - Full Version
752
+ */
753
+
754
+ const getRules = (...args) => riskControlApi.getRules(...args);
755
+ const createRule = (...args) => riskControlApi.createRule(...args);
756
+ const updateRule = (...args) => riskControlApi.updateRule(...args);
757
+ const getEvents = (...args) => riskControlApi.getEvents(...args);
758
+ const checkRisk = (...args) => riskControlApi.checkRisk(...args);
759
+
760
+ export { EventType, RiskAction, RiskControlApiClient, RiskEventTable, RiskLevel, RiskLevelBadge, RiskRuleCard, RiskRulesList, RiskScore, RuleCondition, SUPPORTED_LANGUAGES, checkRisk, createRule, getEvents, getLanguage, getRules, messages, riskControlApi, setLanguage, t, updateRule, useRiskCheck, useRiskEvents, useRiskRules };
761
+ //# sourceMappingURL=index.esm.js.map