@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/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 = 'forgot-password-icon';
3034
- icon.innerHTML = '<div class="icon-glow"></div><svg class="icon-lock" width="56" height="56" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="11" width="14" height="10" rx="2" /><path d="M12 11V7a3 3 0 0 1 3-3h0a3 3 0 0 1 3 3v4" /><circle cx="12" cy="15" r="1" /></svg>';
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 = 'Activation Required';
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 = 'Enter your invitation code to activate your account';
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 Code', 'text', 'Enter invitation code');
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 = 'Activate Account';
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 = 'backToLoginFromInvite';
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 Sign In</span>';
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
  }