@seaverse/auth-sdk 0.3.6 → 0.3.8
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/README.md +35 -3
- package/dist/auth-modal.css +1 -1
- package/dist/index.cjs +272 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +46 -3
- package/dist/index.js +272 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -419,6 +419,21 @@ interface SubmitInviteApplicationRequest {
|
|
|
419
419
|
email: string;
|
|
420
420
|
reason: string;
|
|
421
421
|
}
|
|
422
|
+
/**
|
|
423
|
+
* Apply invite response (successful submission)
|
|
424
|
+
*/
|
|
425
|
+
interface ApplyInviteResponse {
|
|
426
|
+
success: boolean;
|
|
427
|
+
data?: {
|
|
428
|
+
id: string;
|
|
429
|
+
email: string;
|
|
430
|
+
reason: string;
|
|
431
|
+
status: 'pending' | 'approved' | 'rejected';
|
|
432
|
+
created_at: string;
|
|
433
|
+
};
|
|
434
|
+
code?: string;
|
|
435
|
+
error?: string;
|
|
436
|
+
}
|
|
422
437
|
/**
|
|
423
438
|
* Invite application list response
|
|
424
439
|
*/
|
|
@@ -780,6 +795,7 @@ interface BindInviteCodeResponse {
|
|
|
780
795
|
type models_AccountExistsErrorDetails = AccountExistsErrorDetails;
|
|
781
796
|
type models_ApiError = ApiError;
|
|
782
797
|
type models_ApiServiceTokenResponse = ApiServiceTokenResponse;
|
|
798
|
+
type models_ApplyInviteResponse = ApplyInviteResponse;
|
|
783
799
|
type models_BindInviteCodeRequest = BindInviteCodeRequest;
|
|
784
800
|
type models_BindInviteCodeResponse = BindInviteCodeResponse;
|
|
785
801
|
type models_Container = Container;
|
|
@@ -839,7 +855,7 @@ type models_UserInstalledSkillsListResponse = UserInstalledSkillsListResponse;
|
|
|
839
855
|
type models_VideoDetails = VideoDetails;
|
|
840
856
|
declare namespace models {
|
|
841
857
|
export { models_ErrorCode as ErrorCode };
|
|
842
|
-
export type { models_AccountExistsErrorDetails as AccountExistsErrorDetails, models_ApiError as ApiError, models_ApiServiceTokenResponse as ApiServiceTokenResponse, models_BindInviteCodeRequest as BindInviteCodeRequest, models_BindInviteCodeResponse as BindInviteCodeResponse, models_Container as Container, models_ContainerDetailResponse as ContainerDetailResponse, models_ContainerListResponse as ContainerListResponse, models_ContainerStatsResponse as ContainerStatsResponse, models_ConversationStatus as ConversationStatus, models_CreateMarketplaceSkillRequest as CreateMarketplaceSkillRequest, models_CreateVideoShareRequest as CreateVideoShareRequest, models_CreateVideoShareResponse as CreateVideoShareResponse, models_EmailVerificationResponse as EmailVerificationResponse, models_ForgotPasswordRequest as ForgotPasswordRequest, models_ForkProjectRequest as ForkProjectRequest, models_ForkProjectResponse as ForkProjectResponse, models_HealthResponse as HealthResponse, models_HubProject as HubProject, models_HubProjectsListResponse as HubProjectsListResponse, models_InviteApplication as InviteApplication, models_InviteApplicationListResponse as InviteApplicationListResponse, models_InviteCode as InviteCode, models_InviteCodeBindRequest as InviteCodeBindRequest, models_InviteCodeDetailResponse as InviteCodeDetailResponse, models_InviteCodeGenerateResponse as InviteCodeGenerateResponse, models_InviteCodeRequiredErrorData as InviteCodeRequiredErrorData, models_InviteCodeVerifyResponse as InviteCodeVerifyResponse, models_InviteStats as InviteStats, models_InviteStatsResponse as InviteStatsResponse, models_InviteUsage as InviteUsage, models_ListInviteUsagesRequest as ListInviteUsagesRequest, models_ListInviteUsagesResponse as ListInviteUsagesResponse, models_ListInvitesRequest as ListInvitesRequest, models_ListInvitesResponse as ListInvitesResponse, models_LoginRequest as LoginRequest, models_LoginResponse as LoginResponse, models_MarketplaceSkill as MarketplaceSkill, models_MarketplaceSkillsListResponse as MarketplaceSkillsListResponse, models_OAuthAuthorizeRequest as OAuthAuthorizeRequest, models_OAuthAuthorizeResponse as OAuthAuthorizeResponse, models_PublishProjectRequest as PublishProjectRequest, models_PublishSkillRequest as PublishSkillRequest, models_RegisterContainerRequest as RegisterContainerRequest, models_RegisterContainerResponse as RegisterContainerResponse, models_RegisterRequest as RegisterRequest, models_RegisterResponse as RegisterResponse, models_ResetPasswordRequest as ResetPasswordRequest, models_SocialMediaLink as SocialMediaLink, models_SocialMediaLinksResponse as SocialMediaLinksResponse, models_SpeechTokenResponse as SpeechTokenResponse, models_SubmitInviteApplicationRequest as SubmitInviteApplicationRequest, models_SuccessResponse as SuccessResponse, models_TrackAppTypeRequest as TrackAppTypeRequest, models_User as User, models_UserInstalledSkill as UserInstalledSkill, models_UserInstalledSkillsListResponse as UserInstalledSkillsListResponse, models_VideoDetails as VideoDetails };
|
|
858
|
+
export type { models_AccountExistsErrorDetails as AccountExistsErrorDetails, models_ApiError as ApiError, models_ApiServiceTokenResponse as ApiServiceTokenResponse, models_ApplyInviteResponse as ApplyInviteResponse, models_BindInviteCodeRequest as BindInviteCodeRequest, models_BindInviteCodeResponse as BindInviteCodeResponse, models_Container as Container, models_ContainerDetailResponse as ContainerDetailResponse, models_ContainerListResponse as ContainerListResponse, models_ContainerStatsResponse as ContainerStatsResponse, models_ConversationStatus as ConversationStatus, models_CreateMarketplaceSkillRequest as CreateMarketplaceSkillRequest, models_CreateVideoShareRequest as CreateVideoShareRequest, models_CreateVideoShareResponse as CreateVideoShareResponse, models_EmailVerificationResponse as EmailVerificationResponse, models_ForgotPasswordRequest as ForgotPasswordRequest, models_ForkProjectRequest as ForkProjectRequest, models_ForkProjectResponse as ForkProjectResponse, models_HealthResponse as HealthResponse, models_HubProject as HubProject, models_HubProjectsListResponse as HubProjectsListResponse, models_InviteApplication as InviteApplication, models_InviteApplicationListResponse as InviteApplicationListResponse, models_InviteCode as InviteCode, models_InviteCodeBindRequest as InviteCodeBindRequest, models_InviteCodeDetailResponse as InviteCodeDetailResponse, models_InviteCodeGenerateResponse as InviteCodeGenerateResponse, models_InviteCodeRequiredErrorData as InviteCodeRequiredErrorData, models_InviteCodeVerifyResponse as InviteCodeVerifyResponse, models_InviteStats as InviteStats, models_InviteStatsResponse as InviteStatsResponse, models_InviteUsage as InviteUsage, models_ListInviteUsagesRequest as ListInviteUsagesRequest, models_ListInviteUsagesResponse as ListInviteUsagesResponse, models_ListInvitesRequest as ListInvitesRequest, models_ListInvitesResponse as ListInvitesResponse, models_LoginRequest as LoginRequest, models_LoginResponse as LoginResponse, models_MarketplaceSkill as MarketplaceSkill, models_MarketplaceSkillsListResponse as MarketplaceSkillsListResponse, models_OAuthAuthorizeRequest as OAuthAuthorizeRequest, models_OAuthAuthorizeResponse as OAuthAuthorizeResponse, models_PublishProjectRequest as PublishProjectRequest, models_PublishSkillRequest as PublishSkillRequest, models_RegisterContainerRequest as RegisterContainerRequest, models_RegisterContainerResponse as RegisterContainerResponse, models_RegisterRequest as RegisterRequest, models_RegisterResponse as RegisterResponse, models_ResetPasswordRequest as ResetPasswordRequest, models_SocialMediaLink as SocialMediaLink, models_SocialMediaLinksResponse as SocialMediaLinksResponse, models_SpeechTokenResponse as SpeechTokenResponse, models_SubmitInviteApplicationRequest as SubmitInviteApplicationRequest, models_SuccessResponse as SuccessResponse, models_TrackAppTypeRequest as TrackAppTypeRequest, models_User as User, models_UserInstalledSkill as UserInstalledSkill, models_UserInstalledSkillsListResponse as UserInstalledSkillsListResponse, models_VideoDetails as VideoDetails };
|
|
843
859
|
}
|
|
844
860
|
|
|
845
861
|
/**
|
|
@@ -1149,6 +1165,30 @@ declare class SeaVerseBackendAPIClient {
|
|
|
1149
1165
|
* console.log('Account activated:', result.data.user);
|
|
1150
1166
|
*/
|
|
1151
1167
|
bindInviteCode(data: BindInviteCodeRequest, options?: AxiosRequestConfig): Promise<BindInviteCodeResponse>;
|
|
1168
|
+
/**
|
|
1169
|
+
* Submit invite application
|
|
1170
|
+
* Apply for an invitation code by submitting email and reason
|
|
1171
|
+
*
|
|
1172
|
+
* @param data - Invite application request
|
|
1173
|
+
* @param options - Additional axios request options
|
|
1174
|
+
* @returns Application submission response
|
|
1175
|
+
*
|
|
1176
|
+
* @example
|
|
1177
|
+
* // Submit invite application
|
|
1178
|
+
* const result = await client.applyInvite({
|
|
1179
|
+
* email: 'player@example.com',
|
|
1180
|
+
* reason: 'I want to join this amazing platform to build innovative applications.'
|
|
1181
|
+
* });
|
|
1182
|
+
*
|
|
1183
|
+
* if (result.success) {
|
|
1184
|
+
* console.log('Application submitted:', result.data);
|
|
1185
|
+
* console.log('Application ID:', result.data.id);
|
|
1186
|
+
* console.log('Status:', result.data.status); // 'pending'
|
|
1187
|
+
* } else {
|
|
1188
|
+
* console.error('Application failed:', result.error);
|
|
1189
|
+
* }
|
|
1190
|
+
*/
|
|
1191
|
+
applyInvite(data: SubmitInviteApplicationRequest, options?: AxiosRequestConfig): Promise<ApplyInviteResponse>;
|
|
1152
1192
|
/**
|
|
1153
1193
|
* Google OAuth authorization (Backend Proxy Mode)
|
|
1154
1194
|
* Generate OAuth authorization URL for Google login
|
|
@@ -1285,6 +1325,7 @@ interface AuthModalOptions {
|
|
|
1285
1325
|
onLoginSuccess?: (token: string, user: any) => void;
|
|
1286
1326
|
onSignupSuccess?: (token: string, user: any) => void;
|
|
1287
1327
|
onInviteCodeRequired?: (userId: string, email: string) => void;
|
|
1328
|
+
onApplyInviteSuccess?: (applicationId: string, email: string) => void;
|
|
1288
1329
|
onError?: (error: Error) => void;
|
|
1289
1330
|
theme?: 'dark' | 'light';
|
|
1290
1331
|
returnUrl?: string;
|
|
@@ -1312,7 +1353,7 @@ declare class AuthModal {
|
|
|
1312
1353
|
/**
|
|
1313
1354
|
* Show the authentication modal
|
|
1314
1355
|
*/
|
|
1315
|
-
show(initialView?: 'login' | 'signup' | 'forgot' | 'reset-password' | 'invite-code' | 'message'): void;
|
|
1356
|
+
show(initialView?: 'login' | 'signup' | 'forgot' | 'reset-password' | 'invite-code' | 'apply-invite' | 'message'): void;
|
|
1316
1357
|
/**
|
|
1317
1358
|
* Hide the authentication modal
|
|
1318
1359
|
*/
|
|
@@ -1329,6 +1370,7 @@ declare class AuthModal {
|
|
|
1329
1370
|
private createForgotPasswordForm;
|
|
1330
1371
|
private createResetPasswordForm;
|
|
1331
1372
|
private createInviteCodeForm;
|
|
1373
|
+
private createApplyInviteForm;
|
|
1332
1374
|
private createSuccessMessage;
|
|
1333
1375
|
private createFormGroup;
|
|
1334
1376
|
private createPasswordInput;
|
|
@@ -1359,6 +1401,7 @@ declare class AuthModal {
|
|
|
1359
1401
|
private showSuccess;
|
|
1360
1402
|
private showError;
|
|
1361
1403
|
private showWarning;
|
|
1404
|
+
private handleApplyInvite;
|
|
1362
1405
|
private showInfo;
|
|
1363
1406
|
/**
|
|
1364
1407
|
* @deprecated Use showSuccess, showError, showWarning, or showInfo instead
|
|
@@ -1480,4 +1523,4 @@ declare class Toast {
|
|
|
1480
1523
|
}
|
|
1481
1524
|
|
|
1482
1525
|
export { AuthFactory, AuthModal, AuthProvider, BuiltInHooks, ENVIRONMENT_CONFIGS, ErrorCode, SeaVerseBackendAPIClient, Toast, createAuthModal, detectEnvironment, getEnvironmentConfig, models };
|
|
1483
|
-
export type { AccountExistsErrorDetails, ApiError, ApiServiceTokenResponse, AuthModalOptions, AuthModalResult, BindInviteCodeRequest, BindInviteCodeResponse, Container, ContainerDetailResponse, ContainerListResponse, ContainerStatsResponse, ConversationStatus, CreateMarketplaceSkillRequest, CreateVideoShareRequest, CreateVideoShareResponse, EmailVerificationResponse, Environment, EnvironmentConfig, ForgotPasswordRequest, ForkProjectRequest, ForkProjectResponse, HealthResponse, HubProject, HubProjectsListResponse, InviteApplication, InviteApplicationListResponse, InviteCode, InviteCodeBindRequest, InviteCodeDetailResponse, InviteCodeGenerateResponse, InviteCodeRequiredErrorData, InviteCodeVerifyResponse, InviteStats, InviteStatsResponse, InviteUsage, ListInviteUsagesRequest, ListInviteUsagesResponse, ListInvitesRequest, ListInvitesResponse, LoginRequest, LoginResponse, MarketplaceSkill, MarketplaceSkillsListResponse, OAuthAuthorizeRequest, OAuthAuthorizeResponse, PublishProjectRequest, PublishSkillRequest, RegisterContainerRequest, RegisterContainerResponse, RegisterRequest, RegisterResponse, ResetPasswordRequest, RetryOptions, SeaVerseBackendAPIClientOptions, SocialMediaLink, SocialMediaLinksResponse, SpeechTokenResponse, SubmitInviteApplicationRequest, SuccessResponse, ToastOptions, ToastType, TrackAppTypeRequest, User, UserInstalledSkill, UserInstalledSkillsListResponse, VideoDetails };
|
|
1526
|
+
export type { AccountExistsErrorDetails, ApiError, ApiServiceTokenResponse, ApplyInviteResponse, AuthModalOptions, AuthModalResult, BindInviteCodeRequest, BindInviteCodeResponse, Container, ContainerDetailResponse, ContainerListResponse, ContainerStatsResponse, ConversationStatus, CreateMarketplaceSkillRequest, CreateVideoShareRequest, CreateVideoShareResponse, EmailVerificationResponse, Environment, EnvironmentConfig, ForgotPasswordRequest, ForkProjectRequest, ForkProjectResponse, HealthResponse, HubProject, HubProjectsListResponse, InviteApplication, InviteApplicationListResponse, InviteCode, InviteCodeBindRequest, InviteCodeDetailResponse, InviteCodeGenerateResponse, InviteCodeRequiredErrorData, InviteCodeVerifyResponse, InviteStats, InviteStatsResponse, InviteUsage, ListInviteUsagesRequest, ListInviteUsagesResponse, ListInvitesRequest, ListInvitesResponse, LoginRequest, LoginResponse, MarketplaceSkill, MarketplaceSkillsListResponse, OAuthAuthorizeRequest, OAuthAuthorizeResponse, PublishProjectRequest, PublishSkillRequest, RegisterContainerRequest, RegisterContainerResponse, RegisterRequest, RegisterResponse, ResetPasswordRequest, RetryOptions, SeaVerseBackendAPIClientOptions, SocialMediaLink, SocialMediaLinksResponse, SpeechTokenResponse, SubmitInviteApplicationRequest, SuccessResponse, ToastOptions, ToastType, TrackAppTypeRequest, User, UserInstalledSkill, UserInstalledSkillsListResponse, VideoDetails };
|
package/dist/index.js
CHANGED
|
@@ -1637,6 +1637,43 @@ class SeaVerseBackendAPIClient {
|
|
|
1637
1637
|
const response = await this.httpClient.request(config);
|
|
1638
1638
|
return response.data;
|
|
1639
1639
|
}
|
|
1640
|
+
/**
|
|
1641
|
+
* Submit invite application
|
|
1642
|
+
* Apply for an invitation code by submitting email and reason
|
|
1643
|
+
*
|
|
1644
|
+
* @param data - Invite application request
|
|
1645
|
+
* @param options - Additional axios request options
|
|
1646
|
+
* @returns Application submission response
|
|
1647
|
+
*
|
|
1648
|
+
* @example
|
|
1649
|
+
* // Submit invite application
|
|
1650
|
+
* const result = await client.applyInvite({
|
|
1651
|
+
* email: 'player@example.com',
|
|
1652
|
+
* reason: 'I want to join this amazing platform to build innovative applications.'
|
|
1653
|
+
* });
|
|
1654
|
+
*
|
|
1655
|
+
* if (result.success) {
|
|
1656
|
+
* console.log('Application submitted:', result.data);
|
|
1657
|
+
* console.log('Application ID:', result.data.id);
|
|
1658
|
+
* console.log('Status:', result.data.status); // 'pending'
|
|
1659
|
+
* } else {
|
|
1660
|
+
* console.error('Application failed:', result.error);
|
|
1661
|
+
* }
|
|
1662
|
+
*/
|
|
1663
|
+
async applyInvite(data, options) {
|
|
1664
|
+
const config = {
|
|
1665
|
+
method: 'POST',
|
|
1666
|
+
url: `/sdk/v1/auth/apply-invite`,
|
|
1667
|
+
data,
|
|
1668
|
+
headers: {
|
|
1669
|
+
'X-Operation-Id': 'applyInvite',
|
|
1670
|
+
...options?.headers,
|
|
1671
|
+
},
|
|
1672
|
+
...options,
|
|
1673
|
+
};
|
|
1674
|
+
const response = await this.httpClient.request(config);
|
|
1675
|
+
return response.data;
|
|
1676
|
+
}
|
|
1640
1677
|
// ============================================================================
|
|
1641
1678
|
// OAuth APIs
|
|
1642
1679
|
// ============================================================================
|
|
@@ -2691,6 +2728,9 @@ class AuthModal {
|
|
|
2691
2728
|
// Invite code form
|
|
2692
2729
|
const inviteCodeForm = this.createInviteCodeForm();
|
|
2693
2730
|
rightPanel.appendChild(inviteCodeForm);
|
|
2731
|
+
// Apply invite form
|
|
2732
|
+
const applyInviteForm = this.createApplyInviteForm();
|
|
2733
|
+
rightPanel.appendChild(applyInviteForm);
|
|
2694
2734
|
// Success message
|
|
2695
2735
|
const successMessage = this.createSuccessMessage();
|
|
2696
2736
|
rightPanel.appendChild(successMessage);
|
|
@@ -3028,21 +3068,37 @@ class AuthModal {
|
|
|
3028
3068
|
const container = document.createElement('div');
|
|
3029
3069
|
container.id = 'inviteCodeForm';
|
|
3030
3070
|
container.className = 'auth-form-view hidden';
|
|
3031
|
-
// Icon
|
|
3071
|
+
// Icon - 使用星星图标
|
|
3032
3072
|
const icon = document.createElement('div');
|
|
3033
|
-
icon.className = '
|
|
3034
|
-
|
|
3073
|
+
icon.className = 'invite-code-icon';
|
|
3074
|
+
const iconGlow = document.createElement('div');
|
|
3075
|
+
iconGlow.className = 'icon-glow';
|
|
3076
|
+
icon.appendChild(iconGlow);
|
|
3077
|
+
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
3078
|
+
svg.setAttribute('class', 'icon-star');
|
|
3079
|
+
svg.setAttribute('width', '56');
|
|
3080
|
+
svg.setAttribute('height', '56');
|
|
3081
|
+
svg.setAttribute('viewBox', '0 0 24 24');
|
|
3082
|
+
svg.setAttribute('fill', 'none');
|
|
3083
|
+
svg.setAttribute('stroke', 'currentColor');
|
|
3084
|
+
svg.setAttribute('stroke-width', '1.5');
|
|
3085
|
+
svg.setAttribute('stroke-linecap', 'round');
|
|
3086
|
+
svg.setAttribute('stroke-linejoin', 'round');
|
|
3087
|
+
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
3088
|
+
path.setAttribute('d', 'M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z');
|
|
3089
|
+
svg.appendChild(path);
|
|
3090
|
+
icon.appendChild(svg);
|
|
3035
3091
|
container.appendChild(icon);
|
|
3036
3092
|
// Header
|
|
3037
3093
|
const header = document.createElement('div');
|
|
3038
3094
|
header.className = 'auth-form-header';
|
|
3039
3095
|
const title = document.createElement('h2');
|
|
3040
3096
|
title.className = 'auth-form-title';
|
|
3041
|
-
title.textContent = '
|
|
3097
|
+
title.textContent = 'Bind Invitation Code';
|
|
3042
3098
|
const subtitle = document.createElement('p');
|
|
3043
3099
|
subtitle.className = 'auth-form-subtitle';
|
|
3044
3100
|
subtitle.id = 'inviteCodeSubtitle';
|
|
3045
|
-
subtitle.textContent = '
|
|
3101
|
+
subtitle.textContent = 'Complete your registration by entering an invitation code';
|
|
3046
3102
|
header.appendChild(title);
|
|
3047
3103
|
header.appendChild(subtitle);
|
|
3048
3104
|
container.appendChild(header);
|
|
@@ -3050,7 +3106,7 @@ class AuthModal {
|
|
|
3050
3106
|
const form = document.createElement('form');
|
|
3051
3107
|
form.id = 'inviteCodeFormElement';
|
|
3052
3108
|
form.className = 'auth-form';
|
|
3053
|
-
const codeGroup = this.createFormGroup('inviteCodeInput', 'Invitation
|
|
3109
|
+
const codeGroup = this.createFormGroup('inviteCodeInput', 'Invitation code *', 'text', 'Enter your invitation code');
|
|
3054
3110
|
form.appendChild(codeGroup);
|
|
3055
3111
|
const submitBtn = document.createElement('button');
|
|
3056
3112
|
submitBtn.type = 'submit';
|
|
@@ -3058,7 +3114,102 @@ class AuthModal {
|
|
|
3058
3114
|
submitBtn.className = 'btn-auth-primary';
|
|
3059
3115
|
const btnText = document.createElement('span');
|
|
3060
3116
|
btnText.className = 'btn-text';
|
|
3061
|
-
btnText.textContent = '
|
|
3117
|
+
btnText.textContent = 'Get Started';
|
|
3118
|
+
const btnLoader = document.createElement('span');
|
|
3119
|
+
btnLoader.className = 'btn-loader hidden';
|
|
3120
|
+
const spinnerSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
3121
|
+
spinnerSvg.setAttribute('class', 'spinner');
|
|
3122
|
+
spinnerSvg.setAttribute('viewBox', '0 0 24 24');
|
|
3123
|
+
const track = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
3124
|
+
track.setAttribute('class', 'spinner-track');
|
|
3125
|
+
track.setAttribute('cx', '12');
|
|
3126
|
+
track.setAttribute('cy', '12');
|
|
3127
|
+
track.setAttribute('r', '10');
|
|
3128
|
+
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
3129
|
+
circle.setAttribute('class', 'spinner-circle');
|
|
3130
|
+
circle.setAttribute('cx', '12');
|
|
3131
|
+
circle.setAttribute('cy', '12');
|
|
3132
|
+
circle.setAttribute('r', '10');
|
|
3133
|
+
spinnerSvg.appendChild(track);
|
|
3134
|
+
spinnerSvg.appendChild(circle);
|
|
3135
|
+
btnLoader.appendChild(spinnerSvg);
|
|
3136
|
+
submitBtn.appendChild(btnText);
|
|
3137
|
+
submitBtn.appendChild(btnLoader);
|
|
3138
|
+
form.appendChild(submitBtn);
|
|
3139
|
+
container.appendChild(form);
|
|
3140
|
+
// 分隔线
|
|
3141
|
+
const divider = document.createElement('div');
|
|
3142
|
+
divider.className = 'invite-code-divider';
|
|
3143
|
+
divider.textContent = "DON'T HAVE CODE?";
|
|
3144
|
+
container.appendChild(divider);
|
|
3145
|
+
// Apply invite 按钮
|
|
3146
|
+
const applyInviteBtn = document.createElement('button');
|
|
3147
|
+
applyInviteBtn.type = 'button';
|
|
3148
|
+
applyInviteBtn.id = 'showApplyInviteForm';
|
|
3149
|
+
applyInviteBtn.className = 'btn-apply-invite';
|
|
3150
|
+
applyInviteBtn.textContent = 'Apply for access';
|
|
3151
|
+
container.appendChild(applyInviteBtn);
|
|
3152
|
+
return container;
|
|
3153
|
+
}
|
|
3154
|
+
createApplyInviteForm() {
|
|
3155
|
+
const container = document.createElement('div');
|
|
3156
|
+
container.id = 'applyInviteForm';
|
|
3157
|
+
container.className = 'auth-form-view hidden';
|
|
3158
|
+
// Icon
|
|
3159
|
+
const icon = document.createElement('div');
|
|
3160
|
+
icon.className = 'forgot-password-icon';
|
|
3161
|
+
icon.innerHTML = '<div class="icon-glow"></div><svg class="icon-mail" width="56" height="56" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>';
|
|
3162
|
+
container.appendChild(icon);
|
|
3163
|
+
// Header
|
|
3164
|
+
const header = document.createElement('div');
|
|
3165
|
+
header.className = 'auth-form-header';
|
|
3166
|
+
const title = document.createElement('h2');
|
|
3167
|
+
title.className = 'auth-form-title';
|
|
3168
|
+
title.textContent = 'Apply for Invitation Code';
|
|
3169
|
+
const subtitle = document.createElement('p');
|
|
3170
|
+
subtitle.className = 'auth-form-subtitle';
|
|
3171
|
+
subtitle.textContent = 'Tell us why you want to join our platform';
|
|
3172
|
+
header.appendChild(title);
|
|
3173
|
+
header.appendChild(subtitle);
|
|
3174
|
+
container.appendChild(header);
|
|
3175
|
+
// Form
|
|
3176
|
+
const form = document.createElement('form');
|
|
3177
|
+
form.id = 'applyInviteFormElement';
|
|
3178
|
+
form.className = 'auth-form';
|
|
3179
|
+
// Email input
|
|
3180
|
+
const emailGroup = this.createFormGroup('applyInviteEmail', 'Email', 'email', 'Enter your email');
|
|
3181
|
+
form.appendChild(emailGroup);
|
|
3182
|
+
// Reason textarea
|
|
3183
|
+
const reasonGroup = document.createElement('div');
|
|
3184
|
+
reasonGroup.className = 'form-group';
|
|
3185
|
+
const reasonLabel = document.createElement('label');
|
|
3186
|
+
reasonLabel.htmlFor = 'applyInviteReason';
|
|
3187
|
+
reasonLabel.className = 'form-label';
|
|
3188
|
+
reasonLabel.textContent = 'Reason';
|
|
3189
|
+
reasonGroup.appendChild(reasonLabel);
|
|
3190
|
+
const reasonTextarea = document.createElement('textarea');
|
|
3191
|
+
reasonTextarea.id = 'applyInviteReason';
|
|
3192
|
+
reasonTextarea.name = 'reason';
|
|
3193
|
+
reasonTextarea.className = 'form-input';
|
|
3194
|
+
reasonTextarea.placeholder = 'Tell us a bit about yourself and what brings you here...';
|
|
3195
|
+
reasonTextarea.rows = 4;
|
|
3196
|
+
reasonTextarea.required = true;
|
|
3197
|
+
reasonTextarea.minLength = 10;
|
|
3198
|
+
reasonTextarea.maxLength = 500;
|
|
3199
|
+
reasonGroup.appendChild(reasonTextarea);
|
|
3200
|
+
const charCount = document.createElement('div');
|
|
3201
|
+
charCount.className = 'char-count';
|
|
3202
|
+
charCount.textContent = '0/500';
|
|
3203
|
+
reasonGroup.appendChild(charCount);
|
|
3204
|
+
form.appendChild(reasonGroup);
|
|
3205
|
+
// Submit button
|
|
3206
|
+
const submitBtn = document.createElement('button');
|
|
3207
|
+
submitBtn.type = 'submit';
|
|
3208
|
+
submitBtn.id = 'applyInviteButton';
|
|
3209
|
+
submitBtn.className = 'btn-auth-primary';
|
|
3210
|
+
const btnText = document.createElement('span');
|
|
3211
|
+
btnText.className = 'btn-text';
|
|
3212
|
+
btnText.textContent = 'Submit Application';
|
|
3062
3213
|
const btnLoader = document.createElement('span');
|
|
3063
3214
|
btnLoader.className = 'btn-loader hidden';
|
|
3064
3215
|
btnLoader.innerHTML = '<svg class="spinner" viewBox="0 0 24 24"><circle class="spinner-track" cx="12" cy="12" r="10"></circle><circle class="spinner-circle" cx="12" cy="12" r="10"></circle></svg>';
|
|
@@ -3071,9 +3222,9 @@ class AuthModal {
|
|
|
3071
3222
|
footer.className = 'auth-footer forgot-footer';
|
|
3072
3223
|
const backLink = document.createElement('a');
|
|
3073
3224
|
backLink.href = '#';
|
|
3074
|
-
backLink.id = '
|
|
3225
|
+
backLink.id = 'backToInviteCodeFromApply';
|
|
3075
3226
|
backLink.className = 'back-to-login';
|
|
3076
|
-
backLink.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5M12 19l-7-7 7-7"/></svg><span>Back to
|
|
3227
|
+
backLink.innerHTML = '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5M12 19l-7-7 7-7"/></svg><span>Back to Enter Code</span>';
|
|
3077
3228
|
footer.appendChild(backLink);
|
|
3078
3229
|
container.appendChild(footer);
|
|
3079
3230
|
return container;
|
|
@@ -3320,6 +3471,16 @@ class AuthModal {
|
|
|
3320
3471
|
e.preventDefault();
|
|
3321
3472
|
this.switchView('login');
|
|
3322
3473
|
});
|
|
3474
|
+
const showApplyInviteForm = this.modal.querySelector('#showApplyInviteForm');
|
|
3475
|
+
showApplyInviteForm?.addEventListener('click', (e) => {
|
|
3476
|
+
e.preventDefault();
|
|
3477
|
+
this.switchView('apply-invite');
|
|
3478
|
+
});
|
|
3479
|
+
const backToInviteCodeFromApply = this.modal.querySelector('#backToInviteCodeFromApply');
|
|
3480
|
+
backToInviteCodeFromApply?.addEventListener('click', (e) => {
|
|
3481
|
+
e.preventDefault();
|
|
3482
|
+
this.switchView('invite-code');
|
|
3483
|
+
});
|
|
3323
3484
|
// Password toggle
|
|
3324
3485
|
const passwordToggles = this.modal.querySelectorAll('.toggle-password');
|
|
3325
3486
|
passwordToggles.forEach((toggle) => {
|
|
@@ -3354,6 +3515,32 @@ class AuthModal {
|
|
|
3354
3515
|
resetPasswordForm?.addEventListener('submit', (e) => this.handleResetPassword(e));
|
|
3355
3516
|
const inviteCodeForm = this.modal.querySelector('#inviteCodeFormElement');
|
|
3356
3517
|
inviteCodeForm?.addEventListener('submit', (e) => this.handleBindInviteCode(e));
|
|
3518
|
+
const applyInviteForm = this.modal.querySelector('#applyInviteFormElement');
|
|
3519
|
+
applyInviteForm?.addEventListener('submit', (e) => this.handleApplyInvite(e));
|
|
3520
|
+
// Character counter for reason textarea with dynamic progress
|
|
3521
|
+
const reasonTextarea = this.modal.querySelector('#applyInviteReason');
|
|
3522
|
+
const charCount = this.modal.querySelector('.char-count');
|
|
3523
|
+
reasonTextarea?.addEventListener('input', () => {
|
|
3524
|
+
if (charCount) {
|
|
3525
|
+
const length = reasonTextarea.value.length;
|
|
3526
|
+
const maxLength = 500;
|
|
3527
|
+
const percentage = (length / maxLength) * 100;
|
|
3528
|
+
// Update text
|
|
3529
|
+
charCount.textContent = `${length}/${maxLength}`;
|
|
3530
|
+
// Update progress bar width
|
|
3531
|
+
charCount.style.setProperty('--progress-width', `${percentage}%`);
|
|
3532
|
+
// Update progress state
|
|
3533
|
+
if (length < 100) {
|
|
3534
|
+
charCount.setAttribute('data-progress', 'low');
|
|
3535
|
+
}
|
|
3536
|
+
else if (length < 400) {
|
|
3537
|
+
charCount.setAttribute('data-progress', 'medium');
|
|
3538
|
+
}
|
|
3539
|
+
else {
|
|
3540
|
+
charCount.setAttribute('data-progress', 'high');
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
});
|
|
3357
3544
|
// OAuth social login buttons
|
|
3358
3545
|
this.bindSocialLoginButtons();
|
|
3359
3546
|
}
|
|
@@ -3400,7 +3587,7 @@ class AuthModal {
|
|
|
3400
3587
|
switchView(view) {
|
|
3401
3588
|
if (!this.modal)
|
|
3402
3589
|
return;
|
|
3403
|
-
const views = ['loginForm', 'signupForm', 'forgotPasswordForm', 'resetPasswordForm', 'inviteCodeForm', 'authMessage'];
|
|
3590
|
+
const views = ['loginForm', 'signupForm', 'forgotPasswordForm', 'resetPasswordForm', 'inviteCodeForm', 'applyInviteForm', 'authMessage'];
|
|
3404
3591
|
views.forEach((viewId) => {
|
|
3405
3592
|
const element = this.modal.querySelector(`#${viewId}`);
|
|
3406
3593
|
element?.classList.add('hidden');
|
|
@@ -3411,6 +3598,7 @@ class AuthModal {
|
|
|
3411
3598
|
forgot: 'forgotPasswordForm',
|
|
3412
3599
|
'reset-password': 'resetPasswordForm',
|
|
3413
3600
|
'invite-code': 'inviteCodeForm',
|
|
3601
|
+
'apply-invite': 'applyInviteForm',
|
|
3414
3602
|
message: 'authMessage',
|
|
3415
3603
|
};
|
|
3416
3604
|
const targetView = this.modal.querySelector(`#${viewMap[view]}`);
|
|
@@ -3701,6 +3889,80 @@ class AuthModal {
|
|
|
3701
3889
|
showWarning(title, message) {
|
|
3702
3890
|
Toast.warning(title, message);
|
|
3703
3891
|
}
|
|
3892
|
+
async handleApplyInvite(e) {
|
|
3893
|
+
e.preventDefault();
|
|
3894
|
+
const emailInput = this.modal?.querySelector('#applyInviteEmail');
|
|
3895
|
+
const reasonTextarea = this.modal?.querySelector('#applyInviteReason');
|
|
3896
|
+
const submitBtn = this.modal?.querySelector('#applyInviteButton');
|
|
3897
|
+
const btnText = submitBtn?.querySelector('.btn-text');
|
|
3898
|
+
const btnLoader = submitBtn?.querySelector('.btn-loader');
|
|
3899
|
+
if (!emailInput || !reasonTextarea || !submitBtn)
|
|
3900
|
+
return;
|
|
3901
|
+
const email = emailInput.value.trim();
|
|
3902
|
+
const reason = reasonTextarea.value.trim();
|
|
3903
|
+
// Validate inputs
|
|
3904
|
+
if (!email) {
|
|
3905
|
+
this.showError('Invalid Input', 'Please enter your email address');
|
|
3906
|
+
return;
|
|
3907
|
+
}
|
|
3908
|
+
if (!reason || reason.length < 10) {
|
|
3909
|
+
this.showError('Invalid Input', 'Please provide a reason (at least 10 characters)');
|
|
3910
|
+
return;
|
|
3911
|
+
}
|
|
3912
|
+
if (reason.length > 500) {
|
|
3913
|
+
this.showError('Invalid Input', 'Reason must be less than 500 characters');
|
|
3914
|
+
return;
|
|
3915
|
+
}
|
|
3916
|
+
try {
|
|
3917
|
+
// Show loading state
|
|
3918
|
+
submitBtn.disabled = true;
|
|
3919
|
+
btnText?.classList.add('hidden');
|
|
3920
|
+
btnLoader?.classList.remove('hidden');
|
|
3921
|
+
// Call applyInvite API
|
|
3922
|
+
const result = await this.client.applyInvite({
|
|
3923
|
+
email,
|
|
3924
|
+
reason,
|
|
3925
|
+
});
|
|
3926
|
+
if (result.success && result.data) {
|
|
3927
|
+
// Success
|
|
3928
|
+
Toast.success('Application Submitted', 'Your invitation code application has been submitted successfully. We will review it and send you an email.');
|
|
3929
|
+
// Trigger callback if provided
|
|
3930
|
+
if (this.options.onApplyInviteSuccess) {
|
|
3931
|
+
this.options.onApplyInviteSuccess(result.data.id, result.data.email);
|
|
3932
|
+
}
|
|
3933
|
+
// Clear form
|
|
3934
|
+
emailInput.value = '';
|
|
3935
|
+
reasonTextarea.value = '';
|
|
3936
|
+
const charCount = this.modal?.querySelector('.char-count');
|
|
3937
|
+
if (charCount) {
|
|
3938
|
+
charCount.textContent = '0/500';
|
|
3939
|
+
}
|
|
3940
|
+
// Switch back to login after a delay
|
|
3941
|
+
setTimeout(() => {
|
|
3942
|
+
this.switchView('login');
|
|
3943
|
+
}, 2000);
|
|
3944
|
+
}
|
|
3945
|
+
else {
|
|
3946
|
+
// Handle business errors (e.g., APPLICATION_DUPLICATE)
|
|
3947
|
+
const errorMessage = result.error || 'Failed to submit application';
|
|
3948
|
+
this.showError('Application Failed', errorMessage);
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
catch (error) {
|
|
3952
|
+
// Handle network/server errors
|
|
3953
|
+
const errorMessage = error.response?.data?.error || error.message || 'Failed to submit application';
|
|
3954
|
+
this.showError('Application Failed', errorMessage);
|
|
3955
|
+
if (this.options.onError) {
|
|
3956
|
+
this.options.onError(error);
|
|
3957
|
+
}
|
|
3958
|
+
}
|
|
3959
|
+
finally {
|
|
3960
|
+
// Reset loading state
|
|
3961
|
+
submitBtn.disabled = false;
|
|
3962
|
+
btnText?.classList.remove('hidden');
|
|
3963
|
+
btnLoader?.classList.add('hidden');
|
|
3964
|
+
}
|
|
3965
|
+
}
|
|
3704
3966
|
showInfo(title, message) {
|
|
3705
3967
|
Toast.info(title, message);
|
|
3706
3968
|
}
|