ultimate-jekyll-manager 0.0.119 → 0.0.120

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 (28) hide show
  1. package/CLAUDE.md +102 -2
  2. package/README.md +171 -2
  3. package/TODO.md +10 -2
  4. package/_backup/form-manager.backup.js +1020 -0
  5. package/dist/assets/js/libs/auth/pages.js +64 -136
  6. package/dist/assets/js/libs/form-manager.js +643 -775
  7. package/dist/assets/js/pages/account/sections/api-keys.js +37 -52
  8. package/dist/assets/js/pages/account/sections/connections.js +37 -46
  9. package/dist/assets/js/pages/account/sections/delete.js +46 -66
  10. package/dist/assets/js/pages/account/sections/profile.js +37 -56
  11. package/dist/assets/js/pages/account/sections/security.js +100 -126
  12. package/dist/assets/js/pages/admin/notifications/new/index.js +72 -157
  13. package/dist/assets/js/pages/blog/index.js +29 -51
  14. package/dist/assets/js/pages/contact/index.js +110 -144
  15. package/dist/assets/js/pages/download/index.js +38 -86
  16. package/dist/assets/js/pages/oauth2/index.js +17 -17
  17. package/dist/assets/js/pages/payment/checkout/index.js +23 -36
  18. package/dist/assets/js/pages/test/libraries/form-manager/index.js +194 -0
  19. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/auth/signin.html +2 -2
  20. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/auth/signup.html +2 -2
  21. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/contact.html +10 -37
  22. package/dist/defaults/dist/pages/test/libraries/form-manager.html +181 -0
  23. package/dist/gulp/tasks/serve.js +18 -0
  24. package/dist/lib/logger.js +1 -1
  25. package/firebase-debug.log +392 -0
  26. package/package.json +6 -6
  27. package/.playwright-mcp/page-2025-10-22T19-11-27-666Z.png +0 -0
  28. package/.playwright-mcp/page-2025-10-22T19-11-57-357Z.png +0 -0
@@ -1,9 +1,12 @@
1
- // API Keys section module
1
+ /**
2
+ * API Keys Section JavaScript
3
+ */
4
+
5
+ // Libraries
2
6
  import { FormManager } from '__main_assets__/js/libs/form-manager.js';
3
7
  import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';
4
8
 
5
9
  let webManager = null;
6
- let resetApiKeyFormManager = null;
7
10
 
8
11
  // Initialize API Keys section
9
12
  export function init(wm) {
@@ -14,7 +17,9 @@ export function init(wm) {
14
17
 
15
18
  // Load API Keys data
16
19
  export function loadData(account) {
17
- if (!account) return;
20
+ if (!account) {
21
+ return;
22
+ }
18
23
 
19
24
  // Update API key display
20
25
  updateApiKey(account.api?.privateKey);
@@ -44,35 +49,44 @@ function setupButtons() {
44
49
 
45
50
  // Setup reset API key form
46
51
  function setupResetApiKeyForm() {
47
- // Initialize FormManager
48
- resetApiKeyFormManager = new FormManager('#reset-api-key-form', {
49
- autoDisable: true,
50
- showSpinner: true,
51
- allowMultipleSubmissions: false,
52
- submitButtonLoadingText: 'Resetting...'
52
+ const formManager = new FormManager('#reset-api-key-form', {
53
+ allowResubmit: false,
54
+ submittingText: 'Resetting...',
55
+ submittedText: 'Reset!',
53
56
  });
54
57
 
55
- // Handle form submission
56
- resetApiKeyFormManager.addEventListener('submit', async (event) => {
57
- event.preventDefault();
58
-
59
- // 1ms wait
58
+ formManager.on('submit', async () => {
59
+ // 1ms wait for dialog to appear properly
60
60
  await new Promise(resolve => setTimeout(resolve, 1));
61
61
 
62
62
  // Show confirmation dialog
63
63
  if (!confirm('Are you sure you want to reset your API key? This will invalidate your current key and any applications using it will stop working.')) {
64
- return resetApiKeyFormManager.setFormState('ready');
64
+ throw new Error('API key reset cancelled.');
65
65
  }
66
66
 
67
- try {
68
- await handleResetApiKeySubmit();
69
- // Success - FormManager will handle state automatically
70
- resetApiKeyFormManager.setFormState('ready');
71
- } catch (error) {
72
- // Manually show error and reset form state
73
- resetApiKeyFormManager.showError(error);
74
- resetApiKeyFormManager.setFormState('ready');
67
+ // Get server API URL
68
+ const serverApiURL = webManager.getApiUrl() + '/backend-manager';
69
+
70
+ // Make API call to reset API key
71
+ const response = await authorizedFetch(serverApiURL, {
72
+ method: 'POST',
73
+ timeout: 30000,
74
+ response: 'json',
75
+ tries: 2,
76
+ body: {
77
+ command: 'user:regenerate-api-keys',
78
+ },
79
+ });
80
+
81
+ if (!response.privateKey) {
82
+ throw new Error(response.message || 'Failed to reset API key');
75
83
  }
84
+
85
+ // Update the displayed API key
86
+ updateApiKey(response.privateKey);
87
+
88
+ // Show success message
89
+ formManager.showSuccess('API key has been reset successfully!');
76
90
  });
77
91
  }
78
92
 
@@ -104,37 +118,8 @@ async function handleCopyApiKey() {
104
118
  $copyBtn.classList.remove('btn-success');
105
119
  $copyBtn.classList.add('btn-outline-adaptive');
106
120
  }, 2000);
107
-
108
121
  } catch (err) {
109
122
  console.error('Failed to copy API key:', err);
110
123
  webManager.utilities().showNotification('Failed to copy API key', 'danger');
111
124
  }
112
125
  }
113
-
114
- // Handle reset API key form submission
115
- async function handleResetApiKeySubmit() {
116
- // Get server API URL
117
- const serverApiURL = webManager.getApiUrl() + '/backend-manager';
118
-
119
- // Make API call to reset API key
120
- const response = await authorizedFetch(serverApiURL, {
121
- method: 'POST',
122
- timeout: 30000,
123
- response: 'json',
124
- tries: 2,
125
- body: {
126
- command: 'user:regenerate-api-keys',
127
- },
128
- });
129
-
130
- if (response.privateKey) {
131
- // Update the displayed API key
132
- updateApiKey(response.privateKey);
133
-
134
- // Show success message
135
- webManager.utilities().showNotification('API key has been reset successfully', 'success');
136
- } else {
137
- throw new Error(response.message || 'Failed to reset API key');
138
- }
139
- }
140
-
@@ -1,6 +1,10 @@
1
- // Connections section module for OAuth account linking
1
+ /**
2
+ * Connections Section JavaScript - OAuth account linking
3
+ */
4
+
5
+ // Libraries
2
6
  import { FormManager } from '__main_assets__/js/libs/form-manager.js';
3
- import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';;
7
+ import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';
4
8
 
5
9
  let webManager = null;
6
10
  let appData = null;
@@ -22,7 +26,9 @@ export async function init(wm) {
22
26
 
23
27
  // Load connections data
24
28
  export async function loadData(account, sharedAppData) {
25
- if (!account) return;
29
+ if (!account) {
30
+ return;
31
+ }
26
32
 
27
33
  accountData = account;
28
34
  appData = sharedAppData;
@@ -133,7 +139,7 @@ function updateProviderStatus(providerId, userConnection, providerSettings) {
133
139
  $description: !!$description,
134
140
  $form: !!$form,
135
141
  $connectButton: !!$connectButton,
136
- $disconnectButton: !!$disconnectButton
142
+ $disconnectButton: !!$disconnectButton,
137
143
  });
138
144
 
139
145
  const isConnected = userConnection && userConnection.identity;
@@ -146,7 +152,7 @@ function updateProviderStatus(providerId, userConnection, providerSettings) {
146
152
  discord: 'Connect to access Discord community features',
147
153
  github: 'Link your GitHub account for repository access',
148
154
  twitter: 'Share updates and connect with Twitter',
149
- facebook: 'Connect your Facebook account for social features'
155
+ facebook: 'Connect your Facebook account for social features',
150
156
  };
151
157
 
152
158
  // Use provider description or fallback to default
@@ -167,7 +173,7 @@ function updateProviderStatus(providerId, userConnection, providerSettings) {
167
173
  const dateStr = date.toLocaleDateString('en-US', {
168
174
  month: 'short',
169
175
  day: 'numeric',
170
- year: 'numeric'
176
+ year: 'numeric',
171
177
  });
172
178
  statusText = `${displayName} • ${dateStr}`;
173
179
  }
@@ -201,15 +207,17 @@ function updateProviderStatus(providerId, userConnection, providerSettings) {
201
207
 
202
208
  // Get display name for connection
203
209
  function getConnectionDisplayName(connection) {
204
- if (!connection || !connection.identity) return 'Unknown';
210
+ if (!connection || !connection.identity) {
211
+ return 'Unknown';
212
+ }
205
213
 
206
214
  // Try different fields based on provider
207
- return connection.identity.global_name ||
208
- connection.identity.username ||
209
- connection.identity.name ||
210
- connection.identity.email ||
211
- connection.identity.id ||
212
- 'Connected';
215
+ return connection.identity.global_name
216
+ || connection.identity.username
217
+ || connection.identity.name
218
+ || connection.identity.email
219
+ || connection.identity.id
220
+ || 'Connected';
213
221
  }
214
222
 
215
223
  // Initialize FormManager for a provider
@@ -230,22 +238,19 @@ function initializeProviderForm(providerId) {
230
238
 
231
239
  console.log(`Initializing FormManager for ${providerId}`);
232
240
 
233
- // Create new FormManager
234
241
  const formManager = new FormManager(`#${formId}`, {
235
- autoDisable: true,
236
- showSpinner: true
242
+ submittingText: 'Connecting...',
237
243
  });
238
244
 
239
245
  // Store the FormManager instance
240
246
  connectionForms.set(providerId, formManager);
241
247
 
242
248
  // Listen for state changes to update button after FormManager is ready
243
- formManager.addEventListener('statechange', (event) => {
244
- const { status } = event.detail;
245
- console.log(`[DEBUG] ${providerId} - FormManager state changed to:`, status);
249
+ formManager.on('statechange', ({ state }) => {
250
+ console.log(`[DEBUG] ${providerId} - FormManager state changed to:`, state);
246
251
 
247
252
  // When FormManager transitions to ready, update the button status
248
- if (status === 'ready') {
253
+ if (state === 'ready') {
249
254
  console.log(`[DEBUG] ${providerId} - FormManager is ready, updating button status`);
250
255
  const userConnection = accountData?.oauth2?.[providerId];
251
256
  const providerSettings = appData?.oauth2?.[providerId];
@@ -253,37 +258,23 @@ function initializeProviderForm(providerId) {
253
258
  }
254
259
  });
255
260
 
256
- // Handle form submission
257
- formManager.addEventListener('submit', async (event) => {
258
- event.preventDefault();
259
-
260
- const { data, submitButton } = event.detail;
261
+ formManager.on('submit', async ({ data, $submitButton }) => {
261
262
  const provider = data.provider;
262
263
 
263
264
  // Determine action from the clicked button's data-action attribute
264
- const action = submitButton?.getAttribute('data-action');
265
+ const action = $submitButton?.getAttribute('data-action');
265
266
 
266
267
  console.log(`[DEBUG] ${providerId} - Form submitted. Action:`, action, 'Provider:', provider);
267
268
 
268
- try {
269
- if (action === 'connect') {
270
- await handleConnect(provider);
271
- } else if (action === 'disconnect') {
272
- const success = await handleDisconnect(provider);
273
- if (success) {
274
- // Reset form state and update UI after successful disconnect
275
- formManager.setFormState('ready');
276
- // Get provider settings to pass for description display
277
- const providerSettings = appData?.oauth2?.[provider];
278
- updateProviderStatus(provider, null, providerSettings);
279
- }
269
+ if (action === 'connect') {
270
+ await handleConnect(provider);
271
+ } else if (action === 'disconnect') {
272
+ const success = await handleDisconnect(provider);
273
+ if (success) {
274
+ // Get provider settings to pass for description display
275
+ const providerSettings = appData?.oauth2?.[provider];
276
+ updateProviderStatus(provider, null, providerSettings);
280
277
  }
281
-
282
- // Success - FormManager will handle state automatically
283
- } catch (error) {
284
- // Show error and reset form state
285
- formManager.showError(error);
286
- formManager.setFormState('ready');
287
278
  }
288
279
  });
289
280
  }
@@ -311,7 +302,7 @@ async function handleConnect(providerId) {
311
302
  state: 'authorize',
312
303
  scope: scope,
313
304
  referrer: window.location.href,
314
- }
305
+ },
315
306
  },
316
307
  });
317
308
 
@@ -354,7 +345,7 @@ async function handleDisconnect(providerId) {
354
345
  state: 'deauthorize',
355
346
  scope: scope,
356
347
  referrer: window.location.href,
357
- }
348
+ },
358
349
  },
359
350
  });
360
351
 
@@ -1,4 +1,8 @@
1
- // Delete account section module
1
+ /**
2
+ * Delete Account Section JavaScript
3
+ */
4
+
5
+ // Libraries
2
6
  import { FormManager } from '__main_assets__/js/libs/form-manager.js';
3
7
  import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';
4
8
 
@@ -22,15 +26,10 @@ function setupDeleteAccountForm() {
22
26
  return;
23
27
  }
24
28
 
25
- // Initialize FormManager
26
29
  formManager = new FormManager('#delete-account-form', {
27
- autoDisable: true,
28
- showSpinner: true,
29
- validateOnSubmit: false, // We'll handle validation manually due to custom confirmation flow
30
- allowMultipleSubmissions: false,
31
- resetOnSuccess: false,
32
- submitButtonLoadingText: 'Deleting account...',
33
- initialState: 'ready',
30
+ allowResubmit: false,
31
+ submittingText: 'Deleting account...',
32
+ submittedText: 'Account Deleted!',
34
33
  });
35
34
 
36
35
  // Enable/disable delete button based on checkbox
@@ -46,44 +45,30 @@ function setupDeleteAccountForm() {
46
45
  });
47
46
  }
48
47
 
49
- // Listen to FormManager submit event
50
- formManager.addEventListener('submit', handleFormSubmit);
51
- }
52
-
53
- // Handle form submission
54
- async function handleFormSubmit(event) {
55
- // Prevent default FormManager submission
56
- event.preventDefault();
57
-
58
- const formData = event.detail.data;
59
- const $checkbox = document.getElementById('delete-confirm-checkbox');
48
+ formManager.on('submit', async ({ data }) => {
49
+ // Check if checkbox is checked
50
+ if (!$checkbox.checked) {
51
+ throw new Error('Please confirm that you understand this action is permanent.');
52
+ }
60
53
 
61
- // Check if checkbox is checked
62
- if (!$checkbox.checked) {
63
- formManager.showError('Please confirm that you understand this action is permanent.');
64
- formManager.setFormState('ready');
65
- return;
66
- }
54
+ // 1ms wait for dialog to appear properly
55
+ await new Promise(resolve => setTimeout(resolve, 1));
67
56
 
68
- // Show confirmation dialog
69
- const confirmMessage = `Are you absolutely sure you want to delete your account?\n\nThis action CANNOT be undone.\n\nType "DELETE" to confirm:`;
70
- const userInput = prompt(confirmMessage);
57
+ // Show confirmation dialog
58
+ const confirmMessage = `Are you absolutely sure you want to delete your account?\n\nThis action CANNOT be undone.\n\nType "DELETE" to confirm:`;
59
+ const userInput = prompt(confirmMessage);
71
60
 
72
- if (userInput !== 'DELETE') {
73
- formManager.showError('Account deletion cancelled. You must type "DELETE" exactly to confirm.');
74
- formManager.setFormState('ready');
75
- return;
76
- }
61
+ if (userInput !== 'DELETE') {
62
+ throw new Error('Account deletion cancelled. You must type "DELETE" exactly to confirm.');
63
+ }
77
64
 
78
- // Final confirmation
79
- const finalConfirm = confirm('This is your last chance to cancel.\n\nAre you sure you want to permanently delete your account?');
65
+ // Final confirmation
66
+ const finalConfirm = confirm('This is your last chance to cancel.\n\nAre you sure you want to permanently delete your account?');
80
67
 
81
- if (!finalConfirm) {
82
- formManager.setFormState('ready');
83
- return;
84
- }
68
+ if (!finalConfirm) {
69
+ throw new Error('Account deletion cancelled.');
70
+ }
85
71
 
86
- try {
87
72
  // Send delete request to server
88
73
  const response = await authorizedFetch(webManager.getApiUrl(), {
89
74
  method: 'POST',
@@ -93,42 +78,37 @@ async function handleFormSubmit(event) {
93
78
  body: {
94
79
  command: 'user:delete',
95
80
  payload: {
96
- reason: formData.reason || '',
97
- confirmed: true
98
- }
99
- }
81
+ reason: data.reason || '',
82
+ confirmed: true,
83
+ },
84
+ },
100
85
  });
101
86
 
102
87
  console.log('Delete account response:', response);
103
88
 
104
- if (response.success) {
105
- // Show success message
106
- formManager.showSuccess('Your account has been successfully deleted. You will now be signed out.');
107
-
108
- // Sign out the user
109
- await webManager.auth().signOut();
110
-
111
- // Redirect to home page
112
- setTimeout(() => {
113
- window.location.href = '/';
114
- }, 1500);
115
- } else {
89
+ if (!response.success) {
116
90
  throw new Error(response.message || 'Failed to delete account');
117
91
  }
118
- } catch (error) {
119
- console.error('Failed to delete account:', error);
120
- formManager.showError(`Failed to delete account: ${error.message}`);
121
- formManager.setFormState('ready');
122
- }
92
+
93
+ // Show success message
94
+ formManager.showSuccess('Your account has been successfully deleted. You will now be signed out.');
95
+
96
+ // Sign out the user
97
+ await webManager.auth().signOut();
98
+
99
+ // Redirect to home page
100
+ setTimeout(() => {
101
+ window.location.href = '/';
102
+ }, 1500);
103
+ });
123
104
  }
124
105
 
125
106
  // Load delete section data (if needed)
126
- export async function loadData(account) {
107
+ export async function loadData() {
127
108
  // No specific data to load for delete section
128
- // Could potentially show account age or other info here
129
109
  }
130
110
 
131
111
  // Called when section is shown
132
112
  export function onShow() {
133
-
134
- }
113
+ // Nothing needed
114
+ }
@@ -1,4 +1,8 @@
1
- // Profile section module
1
+ /**
2
+ * Profile Section JavaScript
3
+ */
4
+
5
+ // Libraries
2
6
  import { FormManager } from '__main_assets__/js/libs/form-manager.js';
3
7
 
4
8
  let formManager = null;
@@ -13,7 +17,9 @@ export function init(wm) {
13
17
 
14
18
  // Load profile data
15
19
  export function loadData(account, user) {
16
- if (!account) return;
20
+ if (!account) {
21
+ return;
22
+ }
17
23
 
18
24
  // Format birthday if it exists
19
25
  let birthdayValue = '';
@@ -33,34 +39,31 @@ export function loadData(account, user) {
33
39
  const formData = {
34
40
  auth: {
35
41
  email: account.auth?.email || user?.email || '',
36
- uid: user?.uid || account.auth?.uid || ''
42
+ uid: user?.uid || account.auth?.uid || '',
37
43
  },
38
44
  personal: {
39
45
  name: {
40
46
  first: account.personal?.name?.first || '',
41
- last: account.personal?.name?.last || ''
47
+ last: account.personal?.name?.last || '',
42
48
  },
43
49
  birthday: birthdayValue,
44
50
  gender: account.personal?.gender || '',
45
51
  location: {
46
52
  country: account.personal?.location?.country || '',
47
53
  region: account.personal?.location?.region || '',
48
- city: account.personal?.location?.city || ''
54
+ city: account.personal?.location?.city || '',
49
55
  },
50
56
  telephone: {
51
57
  countryCode: phoneCountryCode,
52
- national: account.personal?.telephone?.national?.toString() || ''
53
- }
54
- }
58
+ national: account.personal?.telephone?.national?.toString() || '',
59
+ },
60
+ },
55
61
  };
56
62
 
57
- formManager.setValues(formData);
63
+ formManager.setData(formData);
58
64
 
59
65
  // Set form to ready state now that data is loaded
60
- formManager.setFormState('ready');
61
-
62
- // Avatar and display name are now handled by data-wm-bind in HTML
63
- // since photoURL and displayName are always available from getUser()
66
+ formManager.ready();
64
67
 
65
68
  // Update join date
66
69
  updateJoinDate(user);
@@ -72,29 +75,16 @@ export function loadData(account, user) {
72
75
  // Setup profile form
73
76
  function setupProfileForm() {
74
77
  formManager = new FormManager('#profile-form', {
75
- autoDisable: true,
76
- showSpinner: true,
77
- allowMultipleSubmissions: true,
78
- submitButtonLoadingText: 'Saving...',
79
- initialState: 'loading' // Start in loading state until data loads
78
+ autoReady: false, // Start in initializing state until data loads
79
+ submittingText: 'Saving...',
80
80
  });
81
81
 
82
- // Handle form submission
83
- formManager.addEventListener('submit', async (event) => {
84
- const { data } = event.detail;
85
-
86
- try {
87
- // Update user profile
88
- await updateUserProfile(data);
82
+ formManager.on('submit', async ({ data }) => {
83
+ // Update user profile
84
+ await updateUserProfile(data);
89
85
 
90
- // Show success message
91
- webManager.utilities().showNotification('Profile updated successfully', 'success');
92
-
93
- } catch (error) {
94
- console.error('Failed to update profile:', error);
95
- webManager.utilities().showNotification('Failed to update profile. Please try again.', 'danger');
96
- throw error; // Re-throw to trigger FormManager's error handling
97
- }
86
+ // Show success message
87
+ formManager.showSuccess('Profile updated successfully!');
98
88
  });
99
89
  }
100
90
 
@@ -117,12 +107,12 @@ async function updateUserProfile(data) {
117
107
  const date = new Date(data.personal.birthday);
118
108
  data.personal.birthday = {
119
109
  timestamp: date.toISOString(),
120
- timestampUNIX: Math.floor(date.getTime() / 1000)
110
+ timestampUNIX: Math.floor(date.getTime() / 1000),
121
111
  };
122
112
  } else {
123
113
  data.personal.birthday = {
124
- timestamp: "1970-01-01T00:00:00.000Z",
125
- timestampUNIX: 0
114
+ timestamp: '1970-01-01T00:00:00.000Z',
115
+ timestampUNIX: 0,
126
116
  };
127
117
  }
128
118
 
@@ -151,21 +141,16 @@ async function updateUserProfile(data) {
151
141
  await userDocRef.set(data, { merge: true });
152
142
 
153
143
  console.log('Profile successfully updated in Firestore');
154
-
155
- // Avatar updates automatically via data-wm-bind
156
144
  }
157
145
 
158
- // Avatar functionality removed - photoURL is always available from getUser()
159
- // and is handled via data-wm-bind in the HTML template
160
-
161
-
162
-
163
146
  // Handle copy UID
164
147
  async function handleCopyUid() {
165
148
  const $uidInput = document.getElementById('uid-input');
166
149
  const $copyBtn = document.getElementById('copy-uid-btn');
167
150
 
168
- if (!$uidInput || !$uidInput.value) return;
151
+ if (!$uidInput || !$uidInput.value) {
152
+ return;
153
+ }
169
154
 
170
155
  try {
171
156
  // Use webManager's clipboard utility
@@ -185,29 +170,26 @@ async function handleCopyUid() {
185
170
  $copyBtn.classList.remove('btn-success');
186
171
  $copyBtn.classList.add('btn-outline-adaptive');
187
172
  }, 2000);
188
-
189
173
  } catch (err) {
190
174
  console.error('Failed to copy UID:', err);
191
175
  }
192
176
  }
193
177
 
194
- // Display name functionality removed - displayName is always available from getUser()
195
- // and is handled via data-wm-bind in the HTML template
196
-
197
178
  // Update join date from Firebase user
198
179
  function updateJoinDate(user) {
199
180
  const $joinDate = document.getElementById('profile-join-date');
200
- if (!$joinDate) return;
181
+ if (!$joinDate) {
182
+ return;
183
+ }
201
184
 
202
185
  // Get creation time from Firebase user metadata
203
- // The metadata object has creationTime as a string timestamp
204
186
  const creationTime = user?.metadata?.creationTime;
205
187
 
206
188
  if (creationTime) {
207
189
  const joinDate = new Date(creationTime);
208
190
  const formattedDate = joinDate.toLocaleDateString('en-US', {
209
191
  month: 'long',
210
- year: 'numeric'
192
+ year: 'numeric',
211
193
  });
212
194
 
213
195
  $joinDate.textContent = `Member since ${formattedDate}`;
@@ -228,7 +210,7 @@ function updateRoleBadges(account) {
228
210
  developer: 'badge-coder',
229
211
  moderator: 'badge-moderator',
230
212
  betaTester: 'badge-beta',
231
- vip: 'badge-vip'
213
+ vip: 'badge-vip',
232
214
  };
233
215
 
234
216
  // Show/hide role badges
@@ -246,9 +228,9 @@ function updateRoleBadges(account) {
246
228
  // Show premium badge if subscription is active
247
229
  const $premiumBadge = document.getElementById('badge-premium');
248
230
  if ($premiumBadge) {
249
- const isActive = subscription?.status === 'active' ||
250
- subscription?.status === 'trialing' ||
251
- subscription?.active === true;
231
+ const isActive = subscription?.status === 'active'
232
+ || subscription?.status === 'trialing'
233
+ || subscription?.active === true;
252
234
 
253
235
  if (isActive) {
254
236
  $premiumBadge.classList.remove('d-none');
@@ -257,4 +239,3 @@ function updateRoleBadges(account) {
257
239
  }
258
240
  }
259
241
  }
260
-