@ruzhiai/runid-react 0.1.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,51 @@
1
+ import { type CSSProperties, type ReactNode } from 'react';
2
+ import { RunIDAuth, type RunIDAuthConfig, type LoginConfig, type LoginOrConsentResponse, type LoginResponse, type MeResponse, type SessionInfo, type AuditLogEntry, type WecomAuthURLs, type WechatAuthURLs, type AuthMethod, type RunIDAuthError } from '@ruzhiai/runid-sdk';
3
+ export { RunIDAuth };
4
+ export * from '@ruzhiai/runid-sdk';
5
+ interface RunIDAuthContextValue {
6
+ isAuthenticated: boolean;
7
+ isLoading: boolean;
8
+ me: MeResponse | null;
9
+ loginConfig: LoginConfig | null;
10
+ auth: RunIDAuth | null;
11
+ loadLoginConfig: (options?: {
12
+ return_url?: string;
13
+ invite_code?: string;
14
+ }) => Promise<void>;
15
+ sendPhoneCode: (phone: string) => Promise<void>;
16
+ verifyPhone: (phone: string, code: string) => Promise<LoginOrConsentResponse>;
17
+ sendEmailCode: (email: string) => Promise<void>;
18
+ verifyEmail: (email: string, code: string) => Promise<LoginOrConsentResponse>;
19
+ sendWhatsAppCode: (phone: string) => Promise<void>;
20
+ verifyWhatsApp: (phone: string, code: string) => Promise<LoginOrConsentResponse>;
21
+ getWechatAuthURL: (returnUrl?: string) => Promise<WechatAuthURLs>;
22
+ getWecomAuthURL: (returnUrl?: string) => Promise<WecomAuthURLs>;
23
+ loadMe: () => Promise<void>;
24
+ logout: () => Promise<void>;
25
+ getMySessions: () => Promise<{
26
+ data: SessionInfo[];
27
+ }>;
28
+ revokeMySession: (sessionId: string) => Promise<void>;
29
+ getMyAuditLogs: () => Promise<{
30
+ data: AuditLogEntry[];
31
+ }>;
32
+ }
33
+ interface RunIDAuthProviderProps {
34
+ config: RunIDAuthConfig;
35
+ children: ReactNode;
36
+ autoLoadMe?: boolean;
37
+ }
38
+ export declare function RunIDAuthProvider({ config, children, autoLoadMe, }: RunIDAuthProviderProps): import("react/jsx-runtime").JSX.Element;
39
+ export declare function useRunIDAuth(): RunIDAuthContextValue;
40
+ export interface RunIDLoginWidgetProps {
41
+ config: RunIDAuthConfig;
42
+ returnUrl?: string;
43
+ defaultMethod?: AuthMethod;
44
+ locale?: 'zh-CN' | 'en-US';
45
+ onSuccess: (response: LoginResponse) => void | Promise<void>;
46
+ onError?: (error: RunIDAuthError) => void;
47
+ onOAuthRedirect?: (method: 'wechat' | 'wecom', url: string) => void;
48
+ className?: string;
49
+ style?: CSSProperties;
50
+ }
51
+ export declare function RunIDLoginWidget({ config, returnUrl, defaultMethod, locale, onSuccess, onError, onOAuthRedirect, className, style, }: RunIDLoginWidgetProps): import("react/jsx-runtime").JSX.Element;
package/dist/index.js ADDED
@@ -0,0 +1,473 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { createContext, useContext, useState, useCallback, useEffect, } from 'react';
3
+ import { Tabs, Button, Input, Space, Checkbox, Modal, Typography, Spin, message, Form, } from 'antd';
4
+ import { PhoneOutlined, MailOutlined, MessageOutlined, SafetyOutlined, WechatOutlined, UserOutlined, SendOutlined, } from '@ant-design/icons';
5
+ import { RunIDAuth, isConsentRequiredResponse, parseAuthErrorMessage, } from '@ruzhiai/runid-sdk';
6
+ export { RunIDAuth };
7
+ export * from '@ruzhiai/runid-sdk';
8
+ const { Text, Title, Paragraph } = Typography;
9
+ const RunIDAuthContext = createContext(null);
10
+ export function RunIDAuthProvider({ config, children, autoLoadMe = true, }) {
11
+ const [auth] = useState(() => new RunIDAuth(config));
12
+ const [isAuthenticated, setIsAuthenticated] = useState(false);
13
+ const [isLoading, setIsLoading] = useState(true);
14
+ const [me, setMe] = useState(null);
15
+ const [loginConfig, setLoginConfig] = useState(null);
16
+ const loadMe = useCallback(async () => {
17
+ try {
18
+ const response = await auth.getMe();
19
+ setMe(response);
20
+ setIsAuthenticated(true);
21
+ }
22
+ catch {
23
+ setMe(null);
24
+ setIsAuthenticated(false);
25
+ }
26
+ finally {
27
+ setIsLoading(false);
28
+ }
29
+ }, [auth]);
30
+ useEffect(() => {
31
+ if (autoLoadMe) {
32
+ loadMe();
33
+ }
34
+ else {
35
+ setIsLoading(false);
36
+ }
37
+ }, [loadMe, autoLoadMe]);
38
+ const loadLoginConfig = useCallback(async (options) => {
39
+ const c = await auth.getLoginConfig(options);
40
+ setLoginConfig(c);
41
+ }, [auth]);
42
+ const sendPhoneCode = useCallback(async (phone) => auth.sendPhoneCode(phone), [auth]);
43
+ const verifyPhone = useCallback(async (phone, code) => {
44
+ const response = await auth.verifyPhone(phone, code);
45
+ if (isConsentRequiredResponse(response)) {
46
+ return response;
47
+ }
48
+ setIsAuthenticated(true);
49
+ await loadMe();
50
+ return response;
51
+ }, [auth, loadMe]);
52
+ const sendEmailCode = useCallback(async (email) => auth.sendEmailCode(email), [auth]);
53
+ const verifyEmail = useCallback(async (email, code) => {
54
+ const response = await auth.verifyEmail(email, code);
55
+ if (isConsentRequiredResponse(response)) {
56
+ return response;
57
+ }
58
+ setIsAuthenticated(true);
59
+ await loadMe();
60
+ return response;
61
+ }, [auth, loadMe]);
62
+ const sendWhatsAppCode = useCallback(async (phone) => auth.sendWhatsAppCode(phone), [auth]);
63
+ const verifyWhatsApp = useCallback(async (phone, code) => {
64
+ const response = await auth.verifyWhatsApp(phone, code);
65
+ if (isConsentRequiredResponse(response)) {
66
+ return response;
67
+ }
68
+ setIsAuthenticated(true);
69
+ await loadMe();
70
+ return response;
71
+ }, [auth, loadMe]);
72
+ const getWechatAuthURL = useCallback(async (returnUrl) => auth.getWechatAuthURL(returnUrl), [auth]);
73
+ const getWecomAuthURL = useCallback(async (returnUrl) => auth.getWecomAuthURL(returnUrl), [auth]);
74
+ const logout = useCallback(async () => {
75
+ await auth.logout();
76
+ setMe(null);
77
+ setIsAuthenticated(false);
78
+ }, [auth]);
79
+ const getMySessions = useCallback(async () => auth.getMySessions(), [auth]);
80
+ const revokeMySession = useCallback(async (sessionId) => auth.revokeMySession(sessionId), [auth]);
81
+ const getMyAuditLogs = useCallback(async () => auth.getMyAuditLogs(), [auth]);
82
+ const value = {
83
+ isAuthenticated,
84
+ isLoading,
85
+ me,
86
+ loginConfig,
87
+ auth,
88
+ loadLoginConfig,
89
+ sendPhoneCode,
90
+ verifyPhone,
91
+ sendEmailCode,
92
+ verifyEmail,
93
+ sendWhatsAppCode,
94
+ verifyWhatsApp,
95
+ getWechatAuthURL,
96
+ getWecomAuthURL,
97
+ loadMe,
98
+ logout,
99
+ getMySessions,
100
+ revokeMySession,
101
+ getMyAuditLogs,
102
+ };
103
+ return (_jsx(RunIDAuthContext.Provider, { value: value, children: children }));
104
+ }
105
+ export function useRunIDAuth() {
106
+ const context = useContext(RunIDAuthContext);
107
+ if (!context) {
108
+ throw new Error('useRunIDAuth must be used within a RunIDAuthProvider');
109
+ }
110
+ return context;
111
+ }
112
+ const TEXT = {
113
+ 'zh-CN': {
114
+ wechat: '微信登录',
115
+ wecom: '企业微信',
116
+ phone: '手机号',
117
+ email: '邮箱',
118
+ whatsapp: 'WhatsApp',
119
+ code: '验证码',
120
+ sendCode: '获取验证码',
121
+ resendPrefix: '重发',
122
+ login: '登录',
123
+ verifying: '验证中',
124
+ sending: '发送中',
125
+ agreeTerms: '我已阅读并同意',
126
+ consentTitle: '同意协议后继续',
127
+ consentHintNewUser: '账号尚未完成协议授权,完成后将立即创建账户并登录。',
128
+ consentHintExistingUser: '此账号存在未读条款,请同意后继续登录。',
129
+ cancel: '取消',
130
+ continue: '同意并继续',
131
+ requiredMethodError: '请选择可用的登录方式',
132
+ countryCodePrefix: '区号',
133
+ phonePlaceholder: '请输入手机号',
134
+ emailPlaceholder: '请输入邮箱',
135
+ whatsappPlaceholder: '请输入 WhatsApp 号码',
136
+ codePlaceholder: '请输入验证码',
137
+ mfaHint: '该账号已开启二次验证,请完成下一步认证。',
138
+ },
139
+ 'en-US': {
140
+ wechat: 'WeChat',
141
+ wecom: 'WeCom',
142
+ phone: 'Phone',
143
+ email: 'Email',
144
+ whatsapp: 'WhatsApp',
145
+ code: 'Verification code',
146
+ sendCode: 'Send code',
147
+ resendPrefix: 'Resend',
148
+ login: 'Sign in',
149
+ verifying: 'Verifying',
150
+ sending: 'Sending',
151
+ agreeTerms: 'I have read and agree',
152
+ consentTitle: 'Agree to proceed',
153
+ consentHintNewUser: 'This account requires a first-time consent to proceed and will be created after confirmation.',
154
+ consentHintExistingUser: 'Please confirm the latest agreements before continuing.',
155
+ cancel: 'Cancel',
156
+ continue: 'Agree and continue',
157
+ requiredMethodError: 'No login method is available for this product',
158
+ countryCodePrefix: 'Code',
159
+ phonePlaceholder: 'Enter phone number',
160
+ emailPlaceholder: 'Enter email',
161
+ whatsappPlaceholder: 'Enter WhatsApp number',
162
+ codePlaceholder: 'Enter verification code',
163
+ mfaHint: 'MFA is required before login. Please complete second-factor verification.',
164
+ },
165
+ };
166
+ function isAllowedMethod(config, method) {
167
+ return Boolean(config?.client?.allowed_methods.includes(method));
168
+ }
169
+ function resolveInitialMethod(config, requested) {
170
+ if (requested && isAllowedMethod(config, requested)) {
171
+ return requested;
172
+ }
173
+ if (config.client.allowed_methods.includes('phone'))
174
+ return 'phone';
175
+ if (config.client.allowed_methods.includes('email'))
176
+ return 'email';
177
+ if (config.client.allowed_methods.includes('wecom'))
178
+ return 'wecom';
179
+ if (config.client.allowed_methods.includes('wechat'))
180
+ return 'wechat';
181
+ return 'whatsapp';
182
+ }
183
+ function isMfaResponse(response) {
184
+ return !!(response &&
185
+ typeof response === 'object' &&
186
+ response.mfa_required === true);
187
+ }
188
+ const initialCountdown = {
189
+ phone: 0,
190
+ email: 0,
191
+ whatsapp: 0,
192
+ };
193
+ export function RunIDLoginWidget({ config, returnUrl, defaultMethod, locale = 'zh-CN', onSuccess, onError, onOAuthRedirect, className, style, }) {
194
+ const [messageApi, contextHolder] = message.useMessage();
195
+ const [auth, setAuth] = useState(() => new RunIDAuth({
196
+ ...config,
197
+ defaultReturnURL: returnUrl,
198
+ onAuthSuccess: () => { },
199
+ onAuthError: (error) => {
200
+ const msg = parseAuthErrorMessage(error.payload) || error.message;
201
+ if (msg) {
202
+ messageApi.error(msg);
203
+ }
204
+ onError?.(error);
205
+ },
206
+ }));
207
+ useEffect(() => {
208
+ setAuth(new RunIDAuth({
209
+ ...config,
210
+ defaultReturnURL: returnUrl,
211
+ onAuthSuccess: () => { },
212
+ onAuthError: (error) => {
213
+ const msg = parseAuthErrorMessage(error.payload) || error.message;
214
+ if (msg) {
215
+ messageApi.error(msg);
216
+ }
217
+ onError?.(error);
218
+ },
219
+ }));
220
+ }, [config.baseURL, config.client_id, messageApi, onError, returnUrl]);
221
+ const texts = TEXT[locale];
222
+ const [loginConfig, setLoginConfig] = useState(null);
223
+ const [loadingConfig, setLoadingConfig] = useState(true);
224
+ const [activeMethod, setActiveMethod] = useState('phone');
225
+ const [countdown, setCountdown] = useState(initialCountdown);
226
+ const [isSending, setIsSending] = useState({
227
+ phone: false,
228
+ email: false,
229
+ whatsapp: false,
230
+ });
231
+ const [isSubmitting, setIsSubmitting] = useState(false);
232
+ const [phone, setPhone] = useState('');
233
+ const [email, setEmail] = useState('');
234
+ const [phoneCode, setPhoneCode] = useState('');
235
+ const [emailCode, setEmailCode] = useState('');
236
+ const [whatsapp, setWhatsapp] = useState('');
237
+ const [whatsappCode, setWhatsappCode] = useState('');
238
+ const [countryCode, setCountryCode] = useState('+86');
239
+ const [consentData, setConsentData] = useState(null);
240
+ const [agreed, setAgreed] = useState(false);
241
+ const [processingConsent, setProcessingConsent] = useState(false);
242
+ useEffect(() => {
243
+ let active = true;
244
+ const load = async () => {
245
+ try {
246
+ const loaded = await auth.getLoginConfig({
247
+ return_url: returnUrl,
248
+ });
249
+ if (!active)
250
+ return;
251
+ setLoginConfig(loaded);
252
+ if (loaded.client.phone_country_code) {
253
+ setCountryCode(loaded.client.phone_country_code);
254
+ }
255
+ setActiveMethod(resolveInitialMethod(loaded, defaultMethod));
256
+ }
257
+ catch (error) {
258
+ const runErr = error;
259
+ const msg = parseAuthErrorMessage(runErr.payload) || runErr.message || texts.requiredMethodError;
260
+ messageApi.error(msg);
261
+ onError?.(runErr);
262
+ }
263
+ finally {
264
+ if (active) {
265
+ setLoadingConfig(false);
266
+ }
267
+ }
268
+ };
269
+ load();
270
+ return () => {
271
+ active = false;
272
+ };
273
+ }, [auth, defaultMethod, onError, returnUrl, messageApi, texts.requiredMethodError]);
274
+ const methods = [
275
+ { key: 'phone', visible: isAllowedMethod(loginConfig, 'phone'), icon: _jsx(PhoneOutlined, {}), label: texts.phone },
276
+ { key: 'email', visible: isAllowedMethod(loginConfig, 'email'), icon: _jsx(MailOutlined, {}), label: texts.email },
277
+ { key: 'wechat', visible: isAllowedMethod(loginConfig, 'wechat'), icon: _jsx(WechatOutlined, {}), label: texts.wechat },
278
+ { key: 'wecom', visible: isAllowedMethod(loginConfig, 'wecom'), icon: _jsx(UserOutlined, {}), label: texts.wecom },
279
+ { key: 'whatsapp', visible: isAllowedMethod(loginConfig, 'whatsapp'), icon: _jsx(MessageOutlined, {}), label: texts.whatsapp },
280
+ ].filter((item) => item.visible);
281
+ const setCountDown = useCallback((method, seconds) => {
282
+ setCountdown((prev) => ({ ...prev, [method]: seconds }));
283
+ const interval = setInterval(() => {
284
+ setCountdown((prev) => {
285
+ const next = Math.max(0, prev[method] - 1);
286
+ if (next === 0) {
287
+ clearInterval(interval);
288
+ }
289
+ return { ...prev, [method]: next };
290
+ });
291
+ }, 1000);
292
+ }, []);
293
+ const startTimer = useCallback((method) => {
294
+ setCountDown(method, 60);
295
+ }, [setCountDown]);
296
+ const sendCode = useCallback(async (method) => {
297
+ if (countdown[method] > 0) {
298
+ return;
299
+ }
300
+ try {
301
+ const targetPhone = phone.trim();
302
+ const targetEmail = email.trim();
303
+ const targetWhatsapp = whatsapp.trim();
304
+ setIsSending((prev) => ({ ...prev, [method]: true }));
305
+ if (method === 'phone') {
306
+ const target = `${countryCode}${targetPhone}`;
307
+ if (!target) {
308
+ messageApi.error(texts.phonePlaceholder);
309
+ return;
310
+ }
311
+ await auth.sendPhoneCode(target);
312
+ }
313
+ else if (method === 'email') {
314
+ if (!targetEmail.includes('@')) {
315
+ messageApi.error(texts.emailPlaceholder);
316
+ return;
317
+ }
318
+ await auth.sendEmailCode(targetEmail);
319
+ }
320
+ else {
321
+ if (!targetWhatsapp) {
322
+ messageApi.error(texts.whatsappPlaceholder);
323
+ return;
324
+ }
325
+ await auth.sendWhatsAppCode(targetWhatsapp);
326
+ }
327
+ messageApi.success(texts.sendCode);
328
+ startTimer(method);
329
+ }
330
+ catch (error) {
331
+ const runErr = error;
332
+ const msg = parseAuthErrorMessage(runErr.payload) || runErr.message || `${texts.sendCode}失败`;
333
+ messageApi.error(msg);
334
+ onError?.(runErr);
335
+ }
336
+ finally {
337
+ setIsSending((prev) => ({ ...prev, [method]: false }));
338
+ }
339
+ }, [auth, countryCode, countdown, email, messageApi, onError, phone, startTimer, texts, whatsapp]);
340
+ const openOAuth = useCallback(async (method) => {
341
+ try {
342
+ const urls = method === 'wechat'
343
+ ? await auth.getWechatAuthURL(returnUrl)
344
+ : await auth.getWecomAuthURL(returnUrl);
345
+ const target = urls.auth_url;
346
+ if (onOAuthRedirect) {
347
+ onOAuthRedirect(method, target);
348
+ return;
349
+ }
350
+ window.location.assign(target);
351
+ }
352
+ catch (error) {
353
+ const runErr = error;
354
+ const msg = parseAuthErrorMessage(runErr.payload) || runErr.message || '获取 OAuth 登录地址失败';
355
+ messageApi.error(msg);
356
+ onError?.(runErr);
357
+ }
358
+ }, [auth, onError, onOAuthRedirect, returnUrl, messageApi]);
359
+ const handleVerify = useCallback(async () => {
360
+ if (!loginConfig) {
361
+ messageApi.error(texts.requiredMethodError);
362
+ return;
363
+ }
364
+ setIsSubmitting(true);
365
+ try {
366
+ let response;
367
+ if (activeMethod === 'phone') {
368
+ if (!phone.trim() || !phoneCode.trim()) {
369
+ messageApi.error(texts.phonePlaceholder);
370
+ return;
371
+ }
372
+ response = await auth.verifyPhone(`${countryCode}${phone.trim()}`, phoneCode.trim());
373
+ }
374
+ else if (activeMethod === 'email') {
375
+ if (!email.trim() || !emailCode.trim()) {
376
+ messageApi.error(texts.emailPlaceholder);
377
+ return;
378
+ }
379
+ response = await auth.verifyEmail(email.trim(), emailCode.trim());
380
+ }
381
+ else if (activeMethod === 'whatsapp') {
382
+ if (!whatsapp.trim() || !whatsappCode.trim()) {
383
+ messageApi.error(texts.whatsappPlaceholder);
384
+ return;
385
+ }
386
+ response = await auth.verifyWhatsApp(whatsapp.trim(), whatsappCode.trim());
387
+ }
388
+ else {
389
+ messageApi.error(texts.requiredMethodError);
390
+ return;
391
+ }
392
+ if (isConsentRequiredResponse(response)) {
393
+ setConsentData(response);
394
+ setAgreed(false);
395
+ return;
396
+ }
397
+ if (isMfaResponse(response)) {
398
+ messageApi.error(texts.mfaHint);
399
+ return;
400
+ }
401
+ await onSuccess(response);
402
+ }
403
+ catch (error) {
404
+ const runErr = error;
405
+ const msg = parseAuthErrorMessage(runErr.payload) || runErr.message || '登录失败';
406
+ messageApi.error(msg);
407
+ onError?.(runErr);
408
+ }
409
+ finally {
410
+ setIsSubmitting(false);
411
+ }
412
+ }, [activeMethod, auth, countryCode, email, emailCode, loginConfig, messageApi, onError, onSuccess, phone, phoneCode, texts.emailPlaceholder, texts.phonePlaceholder, texts.requiredMethodError, texts.whatsappPlaceholder, texts.mfaHint, whatsapp, whatsappCode]);
413
+ const submitConsent = useCallback(async () => {
414
+ if (!consentData || !agreed) {
415
+ return;
416
+ }
417
+ setProcessingConsent(true);
418
+ try {
419
+ const agreedIds = consentData.pending_agreements
420
+ .map((agreement) => agreement.id)
421
+ .filter(Boolean);
422
+ const response = await auth.consentAndLogin(consentData.account_id, agreedIds, consentData.login_method || activeMethod, consentData.client_id);
423
+ setConsentData(null);
424
+ setAgreed(false);
425
+ await onSuccess(response);
426
+ }
427
+ catch (error) {
428
+ const runErr = error;
429
+ const msg = parseAuthErrorMessage(runErr.payload) || runErr.message || '协议授权失败';
430
+ messageApi.error(msg);
431
+ onError?.(runErr);
432
+ }
433
+ finally {
434
+ setProcessingConsent(false);
435
+ }
436
+ }, [activeMethod, agreed, auth, consentData, messageApi, onError, onSuccess]);
437
+ if (loadingConfig) {
438
+ return (_jsxs("div", { className: className, style: { ...style, textAlign: 'center' }, children: [contextHolder, _jsx(Spin, {})] }));
439
+ }
440
+ if (!loginConfig) {
441
+ return (_jsxs("div", { className: className, style: style, children: [contextHolder, _jsx(Text, { children: texts.requiredMethodError })] }));
442
+ }
443
+ if (!methods.length) {
444
+ return (_jsxs("div", { className: className, style: style, children: [contextHolder, _jsx(Text, { children: texts.requiredMethodError })] }));
445
+ }
446
+ const tabItems = methods.map((method) => ({
447
+ key: method.key,
448
+ label: (_jsxs("span", { children: [method.icon, _jsx("span", { style: { marginLeft: 6 }, children: method.label })] })),
449
+ }));
450
+ const canSubmit = !isSubmitting &&
451
+ [
452
+ activeMethod === 'phone' ? phone.trim() && phoneCode.trim() :
453
+ activeMethod === 'email' ? email.trim() && emailCode.trim() :
454
+ activeMethod === 'whatsapp' ? whatsapp.trim() && whatsappCode.trim() :
455
+ true,
456
+ ].every(Boolean);
457
+ const consentLink = (link) => {
458
+ const href = link.startsWith('http') || link.startsWith('/') ? link : `https://${link}`;
459
+ window.open(href, '_blank', 'noopener,noreferrer');
460
+ };
461
+ return (_jsxs("div", { className: className, style: style, children: [contextHolder, _jsx(Tabs, { activeKey: activeMethod, items: tabItems, onChange: (value) => setActiveMethod(value), size: "large" }), _jsxs(Space, { direction: "vertical", size: 16, style: { width: '100%' }, children: [activeMethod === 'phone' && (_jsxs(Space, { direction: "vertical", size: 12, style: { width: '100%' }, children: [_jsx(Form.Item, { label: texts.countryCodePrefix, required: true, children: _jsxs(Space.Compact, { style: { width: '100%' }, children: [_jsx(Input, { value: countryCode, onChange: (e) => setCountryCode(e.target.value), style: { width: 90 } }), _jsx(Input, { value: phone, onChange: (e) => setPhone(e.target.value), placeholder: texts.phonePlaceholder })] }) }), _jsx(Form.Item, { label: texts.code, required: true, children: _jsxs(Space.Compact, { style: { width: '100%' }, children: [_jsx(Input, { value: phoneCode, onChange: (e) => setPhoneCode(e.target.value), placeholder: texts.codePlaceholder }), _jsx(Button, { icon: _jsx(SendOutlined, {}), onClick: () => sendCode('phone'), loading: isSending.phone, disabled: countdown.phone > 0, children: countdown.phone > 0 ? `${texts.resendPrefix}(${countdown.phone}s)` : texts.sendCode })] }) })] })), activeMethod === 'email' && (_jsxs(Space, { direction: "vertical", size: 12, style: { width: '100%' }, children: [_jsx(Form.Item, { label: texts.email, required: true, children: _jsx(Input, { value: email, onChange: (e) => setEmail(e.target.value), placeholder: texts.emailPlaceholder }) }), _jsx(Form.Item, { label: texts.code, required: true, children: _jsxs(Space.Compact, { style: { width: '100%' }, children: [_jsx(Input, { value: emailCode, onChange: (e) => setEmailCode(e.target.value), placeholder: texts.codePlaceholder }), _jsx(Button, { onClick: () => sendCode('email'), loading: isSending.email, disabled: countdown.email > 0, children: countdown.email > 0 ? `${texts.resendPrefix}(${countdown.email}s)` : texts.sendCode })] }) })] })), activeMethod === 'whatsapp' && (_jsxs(Space, { direction: "vertical", size: 12, style: { width: '100%' }, children: [_jsx(Form.Item, { label: texts.whatsapp, required: true, children: _jsx(Input, { value: whatsapp, onChange: (e) => setWhatsapp(e.target.value), placeholder: texts.whatsappPlaceholder }) }), _jsx(Form.Item, { label: texts.code, required: true, children: _jsxs(Space.Compact, { style: { width: '100%' }, children: [_jsx(Input, { value: whatsappCode, onChange: (e) => setWhatsappCode(e.target.value), placeholder: texts.codePlaceholder }), _jsx(Button, { onClick: () => sendCode('whatsapp'), loading: isSending.whatsapp, disabled: countdown.whatsapp > 0, children: countdown.whatsapp > 0 ? `${texts.resendPrefix}(${countdown.whatsapp}s)` : texts.sendCode })] }) })] })), activeMethod === 'wechat' && (_jsx(Button, { block: true, size: "large", onClick: () => openOAuth('wechat'), icon: _jsx(WechatOutlined, {}), children: texts.wechat })), activeMethod === 'wecom' && (_jsx(Button, { block: true, size: "large", onClick: () => openOAuth('wecom'), icon: _jsx(UserOutlined, {}), children: texts.wecom })), (activeMethod === 'phone' || activeMethod === 'email' || activeMethod === 'whatsapp') && (_jsx(Button, { type: "primary", size: "large", loading: isSubmitting, disabled: !canSubmit, onClick: handleVerify, children: isSubmitting ? texts.verifying : texts.login }))] }), _jsx(Modal, { open: Boolean(consentData), title: _jsx(Title, { level: 4, children: texts.consentTitle }), footer: null, onCancel: () => {
462
+ setConsentData(null);
463
+ setAgreed(false);
464
+ }, destroyOnClose: true, children: consentData && (_jsxs(Space, { direction: "vertical", size: 12, style: { width: '100%' }, children: [_jsx(Paragraph, { children: consentData.is_new_user
465
+ ? texts.consentHintNewUser
466
+ : texts.consentHintExistingUser }), _jsx(Space, { direction: "vertical", style: { width: '100%' }, children: consentData.pending_agreements.map((agreement) => (_jsx(Space, { align: "center", children: _jsx("a", { href: agreement.url, onClick: (event) => {
467
+ event.preventDefault();
468
+ consentLink(agreement.url);
469
+ }, target: "_blank", rel: "noreferrer", children: agreement.title }) }, agreement.id))) }), _jsx(Checkbox, { checked: agreed, onChange: (e) => setAgreed(e.target.checked), children: _jsxs(Text, { children: [_jsx(SafetyOutlined, {}), " ", texts.agreeTerms] }) }), _jsxs(Space, { direction: "vertical", size: 12, style: { width: '100%' }, children: [_jsx(Button, { type: "primary", block: true, loading: processingConsent, disabled: !agreed, onClick: submitConsent, children: texts.continue }), _jsx(Button, { onClick: () => {
470
+ setConsentData(null);
471
+ setAgreed(false);
472
+ }, block: true, children: texts.cancel })] })] })) })] }));
473
+ }
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@ruzhiai/runid-react",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "dependencies": {
8
+ "@ruzhiai/runid-sdk": "0.1.0"
9
+ },
10
+ "peerDependencies": {
11
+ "react": "^18.0.0",
12
+ "antd": "^6.4.3",
13
+ "@ant-design/icons": "^6.0.0"
14
+ },
15
+ "devDependencies": {
16
+ "typescript": "^5.0.0",
17
+ "@types/react": "^18.0.0"
18
+ },
19
+ "scripts": {
20
+ "build": "tsc",
21
+ "dev": "tsc --watch"
22
+ }
23
+ }