@wix/astro 0.2.15 → 0.2.17

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.
Files changed (112) hide show
  1. package/dist/auth-context.js +0 -1
  2. package/dist/client.js +0 -1
  3. package/dist/components/login-helpers/bi-header-generator.js +0 -1
  4. package/dist/components/login-helpers/iframeUtils.js +0 -1
  5. package/dist/components/login-helpers/login-helpers.js +0 -1
  6. package/dist/components/login.astro +859 -0
  7. package/dist/entrypoints/server.js +0 -1
  8. package/dist/index.js +0 -1
  9. package/dist/integration.d.ts +1 -0
  10. package/dist/integration.js +24 -40
  11. package/dist/loaders/blog.js +0 -1
  12. package/dist/loaders/index.js +0 -1
  13. package/dist/middleware.js +0 -1
  14. package/dist/routes/auth/callback.js +0 -1
  15. package/dist/routes/auth/constants.js +0 -1
  16. package/dist/routes/auth/login.js +0 -1
  17. package/dist/routes/auth/logout-callback.js +0 -1
  18. package/dist/routes/auth/logout.js +0 -1
  19. package/dist/routes/auth/runtime.js +0 -1
  20. package/dist/runtime.js +0 -1
  21. package/dist/vite-plugins/sdk-context.js +2 -2
  22. package/package.json +3 -3
  23. package/dist/components/TailwindEditor/EditorPanel.d.ts +0 -11
  24. package/dist/components/TailwindEditor/EditorPanel.js +0 -86
  25. package/dist/components/TailwindEditor/dev-toolbar-app.d.ts +0 -14
  26. package/dist/components/TailwindEditor/dev-toolbar-app.js +0 -39
  27. package/dist/components/TailwindEditor/dev-toolbar.d.ts +0 -13
  28. package/dist/components/TailwindEditor/dev-toolbar.js +0 -547
  29. package/dist/components/TailwindEditor/index.d.ts +0 -5
  30. package/dist/components/TailwindEditor/index.js +0 -155
  31. package/dist/components/TailwindEditor/integration.d.ts +0 -3
  32. package/dist/components/TailwindEditor/integration.js +0 -18
  33. package/dist/components/TailwindEditor/types.d.ts +0 -35
  34. package/dist/components/TailwindEditor/types.js +0 -2
  35. package/dist/components/TailwindEditor/utils.d.ts +0 -22
  36. package/dist/components/TailwindEditor/utils.js +0 -234
  37. package/dist/dev-toolbar/Editor/EditorPanel.d.ts +0 -14
  38. package/dist/dev-toolbar/Editor/EditorPanel.js +0 -1273
  39. package/dist/dev-toolbar/Editor/classes.d.ts +0 -12
  40. package/dist/dev-toolbar/Editor/classes.js +0 -271
  41. package/dist/dev-toolbar/Editor/dnd.d.ts +0 -34
  42. package/dist/dev-toolbar/Editor/dnd.js +0 -1161
  43. package/dist/dev-toolbar/Editor/index.d.ts +0 -5
  44. package/dist/dev-toolbar/Editor/index.js +0 -136
  45. package/dist/dev-toolbar/Editor/types.d.ts +0 -42
  46. package/dist/dev-toolbar/Editor/types.js +0 -2
  47. package/dist/dev-toolbar/agent-chat.d.ts +0 -14
  48. package/dist/dev-toolbar/agent-chat.js +0 -362
  49. package/dist/dev-toolbar/ai-agent-server.d.ts +0 -1
  50. package/dist/dev-toolbar/ai-agent-server.js +0 -174
  51. package/dist/dev-toolbar/ai-agent.d.ts +0 -2
  52. package/dist/dev-toolbar/ai-agent.js +0 -171
  53. package/dist/dev-toolbar/api-key-form.d.ts +0 -7
  54. package/dist/dev-toolbar/api-key-form.js +0 -138
  55. package/dist/dev-toolbar/astro-toolbar.d.ts +0 -1156
  56. package/dist/dev-toolbar/astro-toolbar.js +0 -29
  57. package/dist/dev-toolbar/editor.d.ts +0 -2
  58. package/dist/dev-toolbar/editor.js +0 -19
  59. package/dist/entrypoints/client-sdk-init.d.ts +0 -1
  60. package/dist/entrypoints/client-sdk-init.js +0 -9
  61. package/dist/entrypoints/image-endpoint.d.ts +0 -1
  62. package/dist/entrypoints/image-endpoint.js +0 -4
  63. package/dist/entrypoints/image-service.d.ts +0 -3
  64. package/dist/entrypoints/image-service.js +0 -35
  65. package/dist/entrypoints/manifest.d.ts +0 -2
  66. package/dist/entrypoints/manifest.js +0 -6
  67. package/dist/extensions.d.ts +0 -1
  68. package/dist/extensions.js +0 -2
  69. package/dist/src/auth-context.d.ts +0 -5
  70. package/dist/src/auth-context.js +0 -3
  71. package/dist/src/client.d.ts +0 -15
  72. package/dist/src/client.js +0 -17
  73. package/dist/src/entrypoints/server.d.ts +0 -13
  74. package/dist/src/entrypoints/server.js +0 -37
  75. package/dist/src/index.d.ts +0 -5
  76. package/dist/src/index.js +0 -5
  77. package/dist/src/integration.d.ts +0 -8
  78. package/dist/src/integration.js +0 -229
  79. package/dist/src/loaders/blog.d.ts +0 -2
  80. package/dist/src/loaders/blog.js +0 -49
  81. package/dist/src/loaders/index.d.ts +0 -2
  82. package/dist/src/loaders/index.js +0 -3
  83. package/dist/src/middleware.d.ts +0 -2
  84. package/dist/src/middleware.js +0 -90
  85. package/dist/src/routes/auth/callback.d.ts +0 -3
  86. package/dist/src/routes/auth/callback.js +0 -53
  87. package/dist/src/routes/auth/constants.d.ts +0 -5
  88. package/dist/src/routes/auth/constants.js +0 -6
  89. package/dist/src/routes/auth/login.d.ts +0 -3
  90. package/dist/src/routes/auth/login.js +0 -27
  91. package/dist/src/routes/auth/logout-callback.d.ts +0 -3
  92. package/dist/src/routes/auth/logout-callback.js +0 -10
  93. package/dist/src/routes/auth/logout.d.ts +0 -3
  94. package/dist/src/routes/auth/logout.js +0 -11
  95. package/dist/src/routes/auth/runtime.d.ts +0 -5
  96. package/dist/src/routes/auth/runtime.js +0 -8
  97. package/dist/src/runtime.d.ts +0 -2
  98. package/dist/src/runtime.js +0 -9
  99. package/dist/src/vite-plugins/sdk-context.d.ts +0 -4
  100. package/dist/src/vite-plugins/sdk-context.js +0 -68
  101. package/dist/utils/assets.d.ts +0 -9
  102. package/dist/utils/assets.js +0 -69
  103. package/dist/utils/generate-routes-json.d.ts +0 -9
  104. package/dist/utils/generate-routes-json.js +0 -237
  105. package/dist/utils/image-config.d.ts +0 -33
  106. package/dist/utils/image-config.js +0 -30
  107. package/dist/utils/index.d.ts +0 -8
  108. package/dist/utils/index.js +0 -59
  109. package/dist/utils/non-server-chunk-detector.d.ts +0 -14
  110. package/dist/utils/non-server-chunk-detector.js +0 -73
  111. package/dist/utils/wasm-module-loader.d.ts +0 -13
  112. package/dist/utils/wasm-module-loader.js +0 -117
@@ -0,0 +1,859 @@
1
+ ---
2
+ ---
3
+
4
+ <div class="login-form-wrapper">
5
+ <login-form>
6
+ <form id="loginForm">
7
+ <div class="form-container">
8
+ <h3 id="formTitle">
9
+ <span id="title-login">
10
+ <slot name="title-login">Log In</slot>
11
+ </span>
12
+ <span id="title-signup" style="display: none;">
13
+ <slot name="title-signup">Sign Up</slot>
14
+ </span>
15
+ <span id="title-reset" style="display: none;">
16
+ <slot name="title-reset">Reset Password</slot>
17
+ </span>
18
+ <span id="title-verify" style="display: none;">
19
+ <slot name="title-verify">Email Verification</slot>
20
+ </span>
21
+ <span id="title-success" style="display: none;">
22
+ <slot name="title-success">Success</slot>
23
+ </span>
24
+ </h3>
25
+
26
+ <!-- Success message with animation -->
27
+ <div id="successMessage" style="display: none;">
28
+ <div class="success-animation">
29
+ <svg class="checkmark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52 52">
30
+ <circle class="checkmark__circle" cx="26" cy="26" r="25" fill="none"/>
31
+ <path class="checkmark__check" fill="none" d="M14.1 27.2l7.1 7.2 16.7-16.8"/>
32
+ </svg>
33
+ </div>
34
+ <p class="success-title">
35
+ <slot name="success-title">Login Successful!</slot>
36
+ </p>
37
+ <p id="successText">
38
+ <slot name="success-message">You are now logged in.</slot>
39
+ </p>
40
+ </div>
41
+
42
+ <div id="pendingMessage" style="display: none;">
43
+ <p id="pendingText"></p>
44
+ <button id="okButton" type="button">
45
+ <slot name="ok-button">OK</slot>
46
+ </button>
47
+ </div>
48
+
49
+ <!-- General Error Message -->
50
+ <div id="errorMessage" style="display: none;">
51
+ <div class="error-container">
52
+ <svg class="error-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
53
+ <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
54
+ </svg>
55
+ <p id="errorText">
56
+ <slot name="error-message">Authentication error. Please try again.</slot>
57
+ </p>
58
+ </div>
59
+ </div>
60
+
61
+ <div id="formFields">
62
+ <!-- Username field (sign up only) -->
63
+ <div id="usernameField" style="display: none;">
64
+ <div class="field-label">
65
+ <label for="username">
66
+ <slot name="username-label">Username</slot>
67
+ </label>
68
+ </div>
69
+ <input
70
+ class={Astro.props["input-class"]}
71
+ id="username"
72
+ name="username"
73
+ type="text"
74
+ required
75
+ />
76
+ </div>
77
+
78
+ <!-- Email field -->
79
+ <div id="emailField">
80
+ <div class="field-label">
81
+ <label for="email">
82
+ <slot name="email-label">Email</slot>
83
+ </label>
84
+ </div>
85
+ <input
86
+ class={Astro.props["input-class"]}
87
+ id="email"
88
+ name="email"
89
+ type="email"
90
+ required
91
+ />
92
+ </div>
93
+
94
+ <!-- Verification code field -->
95
+ <div id="codeField" style="display: none;">
96
+ <div class="field-label">
97
+ <label for="code">
98
+ <slot name="code-label">Code</slot>
99
+ </label>
100
+ </div>
101
+ <input
102
+ class={Astro.props["input-class"]}
103
+ id="code"
104
+ name="code"
105
+ type="number"
106
+ required
107
+ />
108
+ </div>
109
+
110
+ <!-- Password field -->
111
+ <div id="passwordField">
112
+ <div class="field-label">
113
+ <label for="password">
114
+ <slot name="password-label">Password</slot>
115
+ </label>
116
+ </div>
117
+ <input
118
+ class={Astro.props["input-class"]}
119
+ id="password"
120
+ name="password"
121
+ type="password"
122
+ required
123
+ />
124
+ </div>
125
+
126
+ <!-- Forgot password link -->
127
+ <div id="forgotPasswordLink">
128
+ <a id="forgotPassword" href="#">
129
+ <slot name="forgot-password">Forgot password?</slot>
130
+ </a>
131
+ </div>
132
+
133
+ <!-- reCAPTCHA container -->
134
+ <div id="recaptcha-container" style="display: none;"></div>
135
+
136
+ <!-- Submit button -->
137
+ <div class="submit-button-container">
138
+ <button id="submitButton" type="submit" class={Astro.props["button-class"]}>
139
+ <span class="login-text">
140
+ <slot name="button-login">Log In</slot>
141
+ </span>
142
+ <span class="signup-text" style="display: none;">
143
+ <slot name="button-signup">Sign Up</slot>
144
+ </span>
145
+ <span class="reset-text" style="display: none;">
146
+ <slot name="button-reset">Reset</slot>
147
+ </span>
148
+ <span class="verify-text" style="display: none;">
149
+ <slot name="button-verify">Submit</slot>
150
+ </span>
151
+ <span class="loading-text loading-bounce" style="display: none;">
152
+ <slot name="button-loading">Loading</slot><span class="dot">.</span><span class="dot">.</span><span class="dot">.</span>
153
+ </span>
154
+ </button>
155
+ </div>
156
+
157
+ <!-- Toggle login/signup -->
158
+ <div id="toggleStateContainer">
159
+ <span class="login-toggle">
160
+ <slot name="toggle-login-text">Not registered?</slot>
161
+ <a id="toggleState" href="#">
162
+ <slot name="toggle-login-link">Sign up</slot>
163
+ </a>
164
+ </span>
165
+ <span class="signup-toggle" style="display: none;">
166
+ <slot name="toggle-signup-text">Already registered?</slot>
167
+ <a id="toggleState-signup" href="#">
168
+ <slot name="toggle-signup-link">Log in</slot>
169
+ </a>
170
+ </span>
171
+ </div>
172
+ </div>
173
+ </div>
174
+ </form>
175
+ </login-form>
176
+ </div>
177
+
178
+ <style>
179
+ button:disabled {
180
+ opacity: 0.5;
181
+ cursor: not-allowed;
182
+ }
183
+
184
+ /* Success animation styles */
185
+ .success-animation {
186
+ display: flex;
187
+ justify-content: center;
188
+ margin: 20px 0;
189
+ }
190
+
191
+ .checkmark {
192
+ width: 80px;
193
+ height: 80px;
194
+ border-radius: 50%;
195
+ display: block;
196
+ stroke-width: 2;
197
+ stroke: #4BB71B;
198
+ stroke-miterlimit: 10;
199
+ box-shadow: inset 0px 0px 0px #4BB71B;
200
+ animation: fill .4s ease-in-out .4s forwards, scale .3s ease-in-out .9s both;
201
+ }
202
+
203
+ .checkmark__circle {
204
+ stroke-dasharray: 166;
205
+ stroke-dashoffset: 166;
206
+ stroke-width: 2;
207
+ stroke-miterlimit: 10;
208
+ stroke: #4BB71B;
209
+ fill: none;
210
+ animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
211
+ }
212
+
213
+ .checkmark__check {
214
+ transform-origin: 50% 50%;
215
+ stroke-dasharray: 48;
216
+ stroke-dashoffset: 48;
217
+ animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards;
218
+ }
219
+
220
+ @keyframes stroke {
221
+ 100% {
222
+ stroke-dashoffset: 0;
223
+ }
224
+ }
225
+
226
+ @keyframes scale {
227
+ 0%, 100% {
228
+ transform: none;
229
+ }
230
+ 50% {
231
+ transform: scale3d(1.1, 1.1, 1);
232
+ }
233
+ }
234
+
235
+ @keyframes fill {
236
+ 100% {
237
+ box-shadow: inset 0px 0px 0px 30px rgba(75, 183, 27, 0.2);
238
+ }
239
+ }
240
+
241
+
242
+ /* RTL support for Hebrew and other RTL languages */
243
+ :global([dir="rtl"]) .form-container {
244
+ text-align: right;
245
+ }
246
+
247
+ :global([dir="rtl"]) input {
248
+ text-align: right;
249
+ }
250
+
251
+
252
+ /* Loading text on submit button animation */
253
+ .loading-bounce .dot {
254
+ display: inline-block; /* Needed for transform */
255
+ position: relative; /* Or just rely on inline-block default */
256
+ animation: bounce 0.6s infinite ease-in-out alternate;
257
+ }
258
+
259
+ /* Add delays to each dot */
260
+ .loading-bounce .dot:nth-child(1) {
261
+ animation-delay: 0.1s;
262
+ }
263
+
264
+ .loading-bounce .dot:nth-child(2) {
265
+ animation-delay: 0.2s;
266
+ }
267
+
268
+ .loading-bounce .dot:nth-child(3) {
269
+ animation-delay: 0.3s;
270
+ }
271
+
272
+ @keyframes bounce {
273
+ from {
274
+ transform: translateY(0);
275
+ }
276
+ to {
277
+ transform: translateY(-5px); /* Adjust bounce height */
278
+ }
279
+ }
280
+ </style>
281
+
282
+ <script>
283
+ import { authentication, verification, recovery } from '@wix/identity';
284
+ import { getMemberTokensForDirectLogin } from './login-helpers/login-helpers.js';
285
+ import { wixContext } from '@wix/sdk-context';
286
+
287
+ class LoginForm extends HTMLElement {
288
+ constructor() {
289
+ super();
290
+ // States enum
291
+ this.State = {
292
+ LOGIN: 'LOGIN',
293
+ SIGNUP: 'SIGNUP',
294
+ RESET_PASSWORD: 'RESET_PASSWORD',
295
+ EMAIL_VERIFICATION: 'EMAIL_VERIFICATION',
296
+ SUCCESS: 'SUCCESS'
297
+ };
298
+
299
+ // Initialize state variables
300
+ this.state = this.State.LOGIN;
301
+ this.loading = false;
302
+ this.email = '';
303
+ this.code = '';
304
+ this.username = '';
305
+ this.password = '';
306
+ this.pending = { state: false, message: '' };
307
+ this.passwordInvalid = false;
308
+ this.emailInvalid = false;
309
+ this.captcha = '';
310
+ this.error = { state: false, message: '' };
311
+
312
+ // Bind methods to this
313
+ this.resetState = this.resetState.bind(this);
314
+ this.submit = this.submit.bind(this);
315
+ this.updateUI = this.updateUI.bind(this);
316
+ this.handleInputChange = this.handleInputChange.bind(this);
317
+ this.showSuccessMessage = this.showSuccessMessage.bind(this);
318
+ this.showErrorMessage = this.showErrorMessage.bind(this);
319
+ }
320
+
321
+ connectedCallback() {
322
+ // Initial setup
323
+ this.setupEventListenersForStaticElements();
324
+ this.updateUI(); // This will handle dynamic element setup
325
+ }
326
+
327
+ setupEventListenersForStaticElements() {
328
+ // Form submission
329
+ this.querySelector('#loginForm').addEventListener('submit', this.submit);
330
+
331
+ // OK button for messages
332
+ this.querySelector('#okButton').addEventListener('click', () => {
333
+ this.resetState();
334
+ });
335
+
336
+ // Forgot password link
337
+ const forgotPasswordLink = this.querySelector('#forgotPassword');
338
+ if (forgotPasswordLink) {
339
+ forgotPasswordLink.addEventListener('click', (e) => {
340
+ e.preventDefault();
341
+ this.state = this.State.RESET_PASSWORD;
342
+ this.resetState();
343
+ });
344
+ }
345
+
346
+ // Input event listeners for all form fields
347
+ const inputs = this.querySelectorAll('input');
348
+ inputs.forEach(input => {
349
+ input.addEventListener('input', this.handleInputChange);
350
+ });
351
+ }
352
+
353
+ async setupCaptcha() {
354
+ try {
355
+ // Set up recaptcha
356
+ if (!window.grecaptcha && this.state === this.State.SIGNUP) {
357
+ const script = document.createElement('script');
358
+ script.src = "https://www.google.com/recaptcha/enterprise.js";
359
+ document.head.appendChild(script);
360
+
361
+ script.onload = () => {
362
+ window.grecaptcha.enterprise.ready(() => {
363
+ window.grecaptcha.enterprise.render('recaptcha-container', {
364
+ 'sitekey': '6Ld0J8IcAAAAANyrnxzrRlX1xrrdXsOmsepUYosy', // Replace with your actual site key
365
+ 'size': 'normal', // or 'compact' for mobile-friendly
366
+ 'callback': this.onCaptchaChange.bind(this),
367
+ 'expired-callback': () => {
368
+ this.captcha = '';
369
+ this.updateInputValues(); // Update UI without full render
370
+ },
371
+ });
372
+ });
373
+ };
374
+ }
375
+ } catch (error) {
376
+ console.error("Error setting up captcha:", error);
377
+ }
378
+ }
379
+
380
+ resetState() {
381
+ this.loading = false;
382
+ this.pending = { state: false, message: '' };
383
+ this.email = '';
384
+ this.code = '';
385
+ this.passwordInvalid = false;
386
+ this.emailInvalid = false;
387
+ this.username = '';
388
+ this.password = '';
389
+ this.error = { state: false, message: '' };
390
+
391
+ // Reset recaptcha if it exists
392
+ if (this.querySelector('.g-recaptcha') && window.grecaptcha) {
393
+ window.grecaptcha.enterprise.reset();
394
+ }
395
+
396
+ this.updateUI();
397
+ }
398
+
399
+ handleInputChange(event) {
400
+ const { id, value } = event.target;
401
+
402
+ // Clear error message when user starts typing
403
+ if (this.error.state) {
404
+ this.error = { state: false, message: '' };
405
+ this.updateUI();
406
+ }
407
+
408
+ if (id === 'email') {
409
+ this.email = value;
410
+ this.emailInvalid = false;
411
+ } else if (id === 'password') {
412
+ this.password = value;
413
+ this.passwordInvalid = false;
414
+ } else if (id === 'username') {
415
+ this.username = value;
416
+ } else if (id === 'code') {
417
+ this.code = value;
418
+ }
419
+
420
+ // Update UI state without full re-render
421
+ this.updateInputValues();
422
+ }
423
+
424
+ async submit(event) {
425
+ event.preventDefault();
426
+ this.loading = true;
427
+ this.error = { state: false, message: '' }; // Clear any previous errors
428
+ this.updateInputValues(); // Update UI without full render
429
+ this.updateButtonText();
430
+
431
+ let response;
432
+
433
+ try {
434
+ if (this.state === this.State.RESET_PASSWORD) {
435
+ await recovery.sendRecoveryEmail(
436
+ this.email,
437
+ { redirect: { url: window.location.origin, clientId: wixContext['clientId'] as string } }
438
+ );
439
+ this.pending = { message: 'Password reset email sent', state: true };
440
+ this.updateUI(); // Update UI for modal state change
441
+ return;
442
+ }
443
+
444
+ if (this.state === this.State.EMAIL_VERIFICATION) {
445
+ response = await verification.verifyDuringAuthentication(
446
+ this.code,
447
+ { stateToken: this.emailVerificationStateToken },
448
+ );
449
+ } else if (this.state === this.State.LOGIN) {
450
+ response = await authentication.loginV2(
451
+ {
452
+ email: this.email,
453
+ },
454
+ {
455
+ password: this.password,
456
+ }
457
+ );
458
+ } else {
459
+ response = await authentication.registerV2({
460
+ email: this.email,
461
+ },
462
+ {
463
+ password: this.password,
464
+ profile: {
465
+ nickname: this.username,
466
+ },
467
+ captchaTokens: [
468
+ {
469
+ Recaptcha: this.captcha,
470
+ },
471
+ ],
472
+ });
473
+ }
474
+
475
+ // Handle login success
476
+ if (response.state === 'SUCCESS') {
477
+ const tokens = await getMemberTokensForDirectLogin(
478
+ response.sessionToken!
479
+ );
480
+
481
+ // Set cookie
482
+ this.setCookie('wixSession', JSON.stringify(tokens.refreshToken), 2);
483
+
484
+ // Show success animation
485
+ this.showSuccessMessage();
486
+
487
+ // Dispatch a success event that consumers can listen for
488
+ this.dispatchEvent(new CustomEvent('login-success'));
489
+ return;
490
+ }
491
+
492
+ // Handle various states
493
+ if (response.state === 'OWNER_APPROVAL_REQUIRED') {
494
+ this.pending = { message: 'Your account is pending approval', state: true };
495
+ this.updateUI(); // Update UI for state change
496
+ } else if (response.state === 'REQUIRE_EMAIL_VERIFICATION') {
497
+ this.state = this.State.EMAIL_VERIFICATION;
498
+ this.emailVerificationStateToken = response.stateToken;
499
+ this.updateUI(); // Update UI for state change
500
+ } else if (response.state === 'FAILURE') {
501
+ if (response.errorCode === 'invalidPassword') {
502
+ this.passwordInvalid = true;
503
+ this.updateInputValues(); // Update without full render
504
+ } else if (
505
+ response.errorCode === 'invalidEmail' ||
506
+ response.errorCode === 'emailAlreadyExists'
507
+ ) {
508
+ this.emailInvalid = true;
509
+ this.updateInputValues(); // Update without full render
510
+ } else if (response.errorCode === 'resetPassword') {
511
+ this.pending = {
512
+ message: 'Your password requires reset',
513
+ state: true,
514
+ };
515
+ this.updateUI(); // Update UI for state change
516
+ } else {
517
+ // Handle other error codes with a generic message
518
+ this.showErrorMessage();
519
+ const errorElem = this.querySelector('#errorText');
520
+ if (errorElem) {
521
+ errorElem.setAttribute('data-error-code', response.errorCode || 'unknown');
522
+ }
523
+ }
524
+ }
525
+ } catch (error) {
526
+ console.error('Authentication error:', error);
527
+ // Show user-friendly error message
528
+ this.showErrorMessage();
529
+ }
530
+
531
+ // Reset recaptcha
532
+ if (this.querySelector('.g-recaptcha') && window.grecaptcha) {
533
+ window.grecaptcha.reset();
534
+ }
535
+
536
+ this.loading = false;
537
+ this.updateInputValues(); // Update UI without full render
538
+ }
539
+
540
+ // New method to show error message
541
+ showErrorMessage() {
542
+ this.error = { state: true, message: '' };
543
+ this.updateUI();
544
+ }
545
+
546
+ setCookie(name, value, days) {
547
+ // Using a simplified cookie setter for demonstration
548
+ let expires = '';
549
+ if (days) {
550
+ const date = new Date();
551
+ date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
552
+ expires = "; expires=" + date.toUTCString();
553
+ }
554
+ document.cookie = name + "=" + (value || "") + expires + "; path=/";
555
+ }
556
+
557
+ onCaptchaChange(token) {
558
+ this.captcha = token;
559
+ this.updateInputValues(); // Update UI without full render
560
+ }
561
+
562
+ // New method to show success message with animation
563
+ showSuccessMessage() {
564
+ // Update state
565
+ this.state = this.State.SUCCESS;
566
+
567
+ // Update UI to show success message
568
+ this.updateUI();
569
+ }
570
+
571
+ updateUI() {
572
+ // Handle success message visibility first
573
+ const successMessage = this.querySelector('#successMessage');
574
+ if (this.state === this.State.SUCCESS) {
575
+ this.updateTitleVisibility();
576
+
577
+ successMessage.style.display = 'block';
578
+ this.querySelector('#formFields').style.display = 'none';
579
+ this.querySelector('#pendingMessage').style.display = 'none';
580
+ this.querySelector('#errorMessage').style.display = 'none';
581
+ return; // Exit early as we don't need to update other UI parts
582
+ } else {
583
+ successMessage.style.display = 'none';
584
+ }
585
+
586
+ // Update title visibility
587
+ this.updateTitleVisibility();
588
+
589
+ // Handle error message visibility
590
+ const errorMessage = this.querySelector('#errorMessage');
591
+
592
+ if (this.error.state) {
593
+ errorMessage.style.display = 'block';
594
+ } else {
595
+ errorMessage.style.display = 'none';
596
+ }
597
+
598
+ // Handle pending message visibility
599
+ this.updatePendingMessageVisibility();
600
+
601
+ // Update field visibility based on current state
602
+ this.updateFieldVisibility();
603
+
604
+ // Update toggle state link
605
+ this.updateToggleStateLink();
606
+
607
+ // Set up captcha if needed
608
+ if (this.state === this.State.SIGNUP) {
609
+ this.setupCaptcha();
610
+ }
611
+
612
+ // Update input values
613
+ this.updateInputValues();
614
+
615
+ // Update button text
616
+ this.updateButtonText();
617
+ }
618
+
619
+ updateTitleVisibility() {
620
+ // First create a map of all title elements
621
+ const titleMap = {
622
+ [this.State.LOGIN]: this.querySelector('#title-login'),
623
+ [this.State.SIGNUP]: this.querySelector('#title-signup'),
624
+ [this.State.RESET_PASSWORD]: this.querySelector('#title-reset'),
625
+ [this.State.EMAIL_VERIFICATION]: this.querySelector('#title-verify'),
626
+ [this.State.SUCCESS]: this.querySelector('#title-success')
627
+ };
628
+
629
+ // Hide all title elements first
630
+ Object.values(titleMap).forEach(elem => {
631
+ if (elem) elem.style.display = 'none';
632
+ });
633
+
634
+ // Show only the current state's title
635
+ const currentTitleElem = titleMap[this.state];
636
+ if (currentTitleElem) {
637
+ currentTitleElem.style.display = '';
638
+ }
639
+ }
640
+
641
+ updateButtonText() {
642
+ // Hide all button text elements
643
+ const buttonTextElements = {
644
+ login: this.querySelector('.login-text'),
645
+ signup: this.querySelector('.signup-text'),
646
+ reset: this.querySelector('.reset-text'),
647
+ verify: this.querySelector('.verify-text'),
648
+ loading: this.querySelector('.loading-text')
649
+ };
650
+
651
+ // Hide all text elements first
652
+ Object.values(buttonTextElements).forEach(elem => {
653
+ if (elem) elem.style.display = 'none';
654
+ });
655
+
656
+ // Determine which text to show based on state
657
+ const textToShow = this.loading ? 'loading' :
658
+ {
659
+ [this.State.LOGIN]: 'login',
660
+ [this.State.SIGNUP]: 'signup',
661
+ [this.State.RESET_PASSWORD]: 'reset',
662
+ [this.State.EMAIL_VERIFICATION]: 'verify'
663
+ }[this.state];
664
+
665
+ // Show the selected text
666
+ const selectedElement = buttonTextElements[textToShow];
667
+ if (selectedElement) {
668
+ selectedElement.style.display = '';
669
+ }
670
+ }
671
+
672
+ updatePendingMessageVisibility() {
673
+ const pendingMessage = this.querySelector('#pendingMessage');
674
+ const formFields = this.querySelector('#formFields');
675
+
676
+ if (this.pending.state) {
677
+ pendingMessage.style.display = 'block';
678
+ formFields.style.display = 'none';
679
+ this.querySelector('#pendingText').textContent = this.pending.message;
680
+ } else {
681
+ pendingMessage.style.display = 'none';
682
+ formFields.style.display = 'block';
683
+ }
684
+ }
685
+
686
+ updateFieldVisibility() {
687
+ // Username field (only visible during signup)
688
+ const usernameField = this.querySelector('#usernameField');
689
+ const usernameInput = this.querySelector('#username');
690
+ if (this.state === this.State.SIGNUP) {
691
+ usernameField.style.display = 'block';
692
+ usernameInput.setAttribute('required', '');
693
+ } else {
694
+ usernameField.style.display = 'none';
695
+ usernameInput.removeAttribute('required');
696
+ }
697
+
698
+ // Email vs Code field visibility
699
+ const emailField = this.querySelector('#emailField');
700
+ const emailInput = this.querySelector('#email');
701
+ const codeField = this.querySelector('#codeField');
702
+ const codeInput = this.querySelector('#code');
703
+
704
+ if (this.state === this.State.EMAIL_VERIFICATION) {
705
+ emailField.style.display = 'none';
706
+ emailInput.removeAttribute('required');
707
+ codeField.style.display = 'block';
708
+ codeInput.setAttribute('required', '');
709
+ } else {
710
+ emailField.style.display = 'block';
711
+ emailInput.setAttribute('required', '');
712
+ codeField.style.display = 'none';
713
+ codeInput.removeAttribute('required');
714
+ }
715
+
716
+ // Password field visibility
717
+ const passwordField = this.querySelector('#passwordField');
718
+ const passwordInput = this.querySelector('#password');
719
+ if (this.state === this.State.RESET_PASSWORD || this.state === this.State.EMAIL_VERIFICATION) {
720
+ passwordField.style.display = 'none';
721
+ passwordInput.removeAttribute('required');
722
+ } else {
723
+ passwordField.style.display = 'block';
724
+ passwordInput.setAttribute('required', '');
725
+ }
726
+
727
+ // Forgot password link visibility
728
+ const forgotPasswordLink = this.querySelector('#forgotPasswordLink');
729
+ if (this.state === this.State.LOGIN) {
730
+ forgotPasswordLink.style.display = 'block';
731
+ } else {
732
+ forgotPasswordLink.style.display = 'none';
733
+ }
734
+
735
+ // Recaptcha visibility
736
+ const recaptchaContainer = this.querySelector('#recaptcha-container');
737
+ if (this.state === this.State.SIGNUP) {
738
+ recaptchaContainer.style.display = 'block';
739
+ } else {
740
+ recaptchaContainer.style.display = 'none';
741
+ }
742
+ }
743
+
744
+ updateToggleStateLink() {
745
+ const toggleStateContainer = this.querySelector('#toggleStateContainer');
746
+ const loginToggle = this.querySelector('.login-toggle');
747
+ const signupToggle = this.querySelector('.signup-toggle');
748
+
749
+ if (this.state === this.State.RESET_PASSWORD || this.state === this.State.EMAIL_VERIFICATION) {
750
+ toggleStateContainer.style.display = 'none';
751
+ } else {
752
+ toggleStateContainer.style.display = 'block';
753
+
754
+ // Show the appropriate toggle based on state
755
+ if (this.state === this.State.LOGIN) {
756
+ loginToggle.style.display = '';
757
+ signupToggle.style.display = 'none';
758
+
759
+ // Add event listener to login toggle
760
+ const toggleLink = loginToggle.querySelector('#toggleState');
761
+ if (toggleLink) {
762
+ toggleLink.addEventListener('click', (e) => {
763
+ e.preventDefault();
764
+ this.state = this.State.SIGNUP;
765
+ this.updateUI();
766
+ });
767
+ }
768
+ } else {
769
+ loginToggle.style.display = 'none';
770
+ signupToggle.style.display = '';
771
+
772
+ // Add event listener to signup toggle
773
+ const toggleLink = signupToggle.querySelector('#toggleState-signup');
774
+ if (toggleLink) {
775
+ toggleLink.addEventListener('click', (e) => {
776
+ e.preventDefault();
777
+ this.state = this.State.LOGIN;
778
+ this.updateUI();
779
+ });
780
+ }
781
+ }
782
+ }
783
+ }
784
+
785
+ updateInputValues() {
786
+ // Update email input
787
+ const emailInput = this.querySelector('#email');
788
+ if (emailInput) {
789
+ emailInput.value = this.email;
790
+ if (this.emailInvalid) {
791
+ emailInput.classList.add('error');
792
+ } else {
793
+ emailInput.classList.remove('error');
794
+ }
795
+ }
796
+
797
+ // Update password input
798
+ const passwordInput = this.querySelector('#password');
799
+ if (passwordInput) {
800
+ passwordInput.value = this.password;
801
+ if (this.passwordInvalid) {
802
+ passwordInput.classList.add('error');
803
+ } else {
804
+ passwordInput.classList.remove('error');
805
+ }
806
+ }
807
+
808
+ // Update username input
809
+ const usernameInput = this.querySelector('#username');
810
+ if (usernameInput) {
811
+ usernameInput.value = this.username;
812
+ }
813
+
814
+ // Update code input
815
+ const codeInput = this.querySelector('#code');
816
+ if (codeInput) {
817
+ codeInput.value = this.code;
818
+ }
819
+
820
+ // Update submit button state
821
+ const submitButton = this.querySelector('#submitButton');
822
+ if (submitButton) {
823
+ let shouldDisable =
824
+ (!this.email && this.state !== this.State.EMAIL_VERIFICATION) ||
825
+ (!this.password &&
826
+ this.state !== this.State.RESET_PASSWORD &&
827
+ this.state !== this.State.EMAIL_VERIFICATION) ||
828
+ this.loading;
829
+
830
+ // Add captcha check for signup
831
+ if (this.state === this.State.SIGNUP) {
832
+ // For signup, require username and captcha as well
833
+ shouldDisable = shouldDisable ||
834
+ !this.username ||
835
+ !this.captcha; // Make button disabled if captcha is not completed
836
+ }
837
+
838
+ submitButton.disabled = shouldDisable;
839
+ }
840
+ }
841
+
842
+ restoreFocus(activeElementId) {
843
+ if (activeElementId) {
844
+ const elementToFocus = this.querySelector(`#${activeElementId}`);
845
+ if (elementToFocus) {
846
+ elementToFocus.focus();
847
+
848
+ // If it's an input, place cursor at the end
849
+ if (elementToFocus.tagName === 'INPUT' && (elementToFocus.type === 'text' || elementToFocus.type === 'email')) {
850
+ elementToFocus.selectionStart = elementToFocus.selectionEnd = elementToFocus.value.length;
851
+ }
852
+ }
853
+ }
854
+ }
855
+ }
856
+
857
+ // Define the custom element
858
+ customElements.define('login-form', LoginForm);
859
+ </script>