ultimate-jekyll-manager 0.0.117 → 0.0.118

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.
@@ -1,4 +1,4 @@
1
- import fetch from 'wonderful-fetch';
1
+ import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';
2
2
 
3
3
  // Auth Module
4
4
  export default function (Manager, options) {
@@ -166,9 +166,6 @@ function setAnalyticsUserId(user) {
166
166
  // Send user metadata to server (affiliate, UTM params, etc.)
167
167
  async function sendUserSignupMetadata(user, webManager) {
168
168
  try {
169
- // Get the auth token
170
- const token = await webManager.auth().getIdToken();
171
-
172
169
  // Get affiliate data from storage
173
170
  const affiliateData = webManager.storage().get('marketing.affiliate', null);
174
171
  const utmData = webManager.storage().get('marketing.utm', null);
@@ -193,11 +190,10 @@ async function sendUserSignupMetadata(user, webManager) {
193
190
  // Get server API URL
194
191
  const serverApiURL = webManager.getApiUrl() + '/backend-manager';
195
192
 
196
- // Make API call to reset API key
197
- const response = await fetch(serverApiURL, {
193
+ // Make API call to send signup metadata
194
+ const response = await authorizedFetch(serverApiURL, {
198
195
  method: 'POST',
199
196
  body: {
200
- authenticationToken: token,
201
197
  command: 'user:sign-up',
202
198
  payload: payload
203
199
  }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Prerendered Icons Library
3
+ * Retrieves pre-rendered icon HTML from the frontmatter icon system
4
+ */
5
+
6
+ /**
7
+ * Get pre-rendered icon by name from frontmatter icon system
8
+ * @param {string} iconName - Name of the icon to retrieve (matches data-icon attribute)
9
+ * @returns {string} Icon HTML or empty string if not found
10
+ *
11
+ * @example
12
+ * import { getPrerenderedIcon } from '__main_assets__/js/libs/prerendered-icons.js';
13
+ *
14
+ * // Get an icon
15
+ * const appleIcon = getPrerenderedIcon('apple');
16
+ */
17
+ export function getPrerenderedIcon(iconName) {
18
+ // Query the global prerendered icons container
19
+ const $iconTemplate = document.querySelector(`#prerendered-icons [data-icon="${iconName}"]`);
20
+
21
+ if ($iconTemplate) {
22
+ return $iconTemplate.innerHTML;
23
+ }
24
+
25
+ // Return empty string if not found
26
+ return '';
27
+ }
@@ -82,7 +82,7 @@ async function initializeAccount() {
82
82
  {
83
83
  // Check for test subscription parameter
84
84
  const urlParams = new URLSearchParams(window.location.search);
85
- const testSubscription = urlParams.get('_test_subscription');
85
+ const testSubscription = urlParams.get('_dev_subscription');
86
86
 
87
87
  if (testSubscription && webManager.isDevelopment()) {
88
88
  try {
@@ -1,6 +1,6 @@
1
1
  // API Keys section module
2
2
  import { FormManager } from '__main_assets__/js/libs/form-manager.js';
3
- import fetch from 'wonderful-fetch';
3
+ import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';
4
4
 
5
5
  let webManager = null;
6
6
  let resetApiKeyFormManager = null;
@@ -113,20 +113,16 @@ async function handleCopyApiKey() {
113
113
 
114
114
  // Handle reset API key form submission
115
115
  async function handleResetApiKeySubmit() {
116
- // Get authentication token
117
- const token = await webManager.auth().getIdToken();
118
-
119
116
  // Get server API URL
120
117
  const serverApiURL = webManager.getApiUrl() + '/backend-manager';
121
118
 
122
119
  // Make API call to reset API key
123
- const response = await fetch(serverApiURL, {
120
+ const response = await authorizedFetch(serverApiURL, {
124
121
  method: 'POST',
125
122
  timeout: 30000,
126
123
  response: 'json',
127
124
  tries: 2,
128
125
  body: {
129
- authenticationToken: token,
130
126
  command: 'user:regenerate-api-keys',
131
127
  },
132
128
  });
@@ -1,6 +1,6 @@
1
1
  // Connections section module for OAuth account linking
2
2
  import { FormManager } from '__main_assets__/js/libs/form-manager.js';
3
- import fetch from 'wonderful-fetch';
3
+ import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';;
4
4
 
5
5
  let webManager = null;
6
6
  let appData = null;
@@ -32,6 +32,8 @@ export async function loadData(account, sharedAppData) {
32
32
 
33
33
  // Display available and connected OAuth providers
34
34
  function displayConnections() {
35
+ console.log('[DEBUG] displayConnections() called');
36
+
35
37
  // Hide loading state
36
38
  const $loading = document.getElementById('connections-loading');
37
39
  if ($loading) {
@@ -40,6 +42,7 @@ function displayConnections() {
40
42
 
41
43
  // Check if appData is loaded
42
44
  if (!appData) {
45
+ console.log('[DEBUG] No appData, showing loading state');
43
46
  // Show loading if no app data yet
44
47
  if ($loading) {
45
48
  $loading.classList.remove('d-none');
@@ -51,36 +54,51 @@ function displayConnections() {
51
54
  const availableProviders = appData?.oauth2 || {};
52
55
  const userConnections = accountData?.oauth2 || {};
53
56
 
57
+ console.log('[DEBUG] Available OAuth providers:', availableProviders);
58
+ console.log('[DEBUG] User connections:', userConnections);
59
+
54
60
  // Check if any providers are configured
55
61
  let hasEnabledProviders = false;
56
62
 
57
63
  // Process each supported provider
58
64
  supportedProviders.forEach(providerId => {
65
+ console.log(`[DEBUG] Processing provider: ${providerId}`);
66
+
59
67
  const providerSettings = availableProviders[providerId];
60
68
  const $providerElement = document.getElementById(`connection-${providerId}`);
61
69
 
70
+ console.log(`[DEBUG] ${providerId} - providerSettings:`, providerSettings);
71
+ console.log(`[DEBUG] ${providerId} - providerSettings.enabled:`, providerSettings?.enabled);
72
+ console.log(`[DEBUG] ${providerId} - $providerElement exists:`, !!$providerElement);
73
+
62
74
  if (!$providerElement) {
75
+ console.warn(`[DEBUG] ${providerId} - Provider element not found in DOM`);
63
76
  return;
64
77
  }
65
78
 
66
79
  // Check if provider is enabled
67
80
  const isEnabled = providerSettings && providerSettings.enabled !== false;
81
+ console.log(`[DEBUG] ${providerId} - isEnabled:`, isEnabled);
68
82
 
69
83
  if (isEnabled) {
70
84
  hasEnabledProviders = true;
71
85
 
72
- // Show the provider element
86
+ // Show the provider element first
87
+ console.log(`[DEBUG] ${providerId} - Removing d-none class`);
73
88
  $providerElement.classList.remove('d-none');
74
89
 
75
- // CRITICAL: Update button status BEFORE initializing FormManager
76
- // This ensures FormManager stores the CORRECT button state from the start
90
+ // Initialize FormManager for this provider before updating status
91
+ console.log(`[DEBUG] ${providerId} - About to initialize FormManager`);
92
+ initializeProviderForm(providerId);
93
+
94
+ // Update provider status based on user connection
77
95
  const userConnection = userConnections[providerId];
96
+ console.log(`[DEBUG] ${providerId} - userConnection:`, userConnection);
97
+ console.log(`[DEBUG] ${providerId} - About to update provider status`);
78
98
  updateProviderStatus(providerId, userConnection, providerSettings);
79
-
80
- // Initialize FormManager AFTER setting correct button state
81
- initializeProviderForm(providerId);
82
99
  } else {
83
100
  // Hide disabled providers
101
+ console.log(`[DEBUG] ${providerId} - Provider disabled, adding d-none class`);
84
102
  $providerElement.classList.add('d-none');
85
103
  }
86
104
  });
@@ -98,14 +116,28 @@ function displayConnections() {
98
116
 
99
117
  // Update provider status display
100
118
  function updateProviderStatus(providerId, userConnection, providerSettings) {
119
+ console.log(`[DEBUG] updateProviderStatus() called for ${providerId}`);
120
+ console.log(`[DEBUG] ${providerId} - userConnection:`, userConnection);
121
+ console.log(`[DEBUG] ${providerId} - userConnection truthy:`, !!userConnection);
122
+ console.log(`[DEBUG] ${providerId} - userConnection.identity:`, userConnection?.identity);
123
+ console.log(`[DEBUG] ${providerId} - providerSettings:`, providerSettings);
124
+
101
125
  const $status = document.getElementById(`${providerId}-connection-status`);
102
126
  const $description = document.getElementById(`${providerId}-connection-description`);
103
127
  const $form = document.getElementById(`connection-form-${providerId}`);
104
- const $button = $form?.querySelector('button[type="submit"]');
105
- const $buttonText = $button?.querySelector('.button-text');
106
- const $action = $form?.querySelector('input[name="action"]');
128
+ const $connectButton = $form?.querySelector('button[data-action="connect"]');
129
+ const $disconnectButton = $form?.querySelector('button[data-action="disconnect"]');
130
+
131
+ console.log(`[DEBUG] ${providerId} - DOM elements found:`, {
132
+ $status: !!$status,
133
+ $description: !!$description,
134
+ $form: !!$form,
135
+ $connectButton: !!$connectButton,
136
+ $disconnectButton: !!$disconnectButton
137
+ });
107
138
 
108
139
  const isConnected = userConnection && userConnection.identity;
140
+ console.log(`[DEBUG] ${providerId} - isConnected:`, isConnected);
109
141
 
110
142
  // Always show description on the left
111
143
  if ($description) {
@@ -117,9 +149,8 @@ function updateProviderStatus(providerId, userConnection, providerSettings) {
117
149
  facebook: 'Connect your Facebook account for social features'
118
150
  };
119
151
 
120
- const descriptionText = providerSettings?.description
121
- || defaultDescriptions[providerId]
122
- || `Connect your ${providerId.charAt(0).toUpperCase() + providerId.slice(1)} account`;
152
+ // Use provider description or fallback to default
153
+ const descriptionText = providerSettings?.description || defaultDescriptions[providerId] || `Connect your ${providerId.charAt(0).toUpperCase() + providerId.slice(1)} account`;
123
154
  $description.textContent = descriptionText;
124
155
  $description.classList.remove('d-none');
125
156
  }
@@ -144,39 +175,26 @@ function updateProviderStatus(providerId, userConnection, providerSettings) {
144
175
  $status.textContent = statusText;
145
176
  $status.classList.remove('d-none');
146
177
  } else {
147
- // Not connected - hide status under button
178
+ // Not connected - hide status text
148
179
  $status.textContent = '';
149
180
  $status.classList.add('d-none');
150
181
  }
151
182
  }
152
183
 
153
- if ($button && $buttonText && $action) {
184
+ // Show/hide appropriate button
185
+ if ($connectButton && $disconnectButton) {
186
+ console.log(`[DEBUG] ${providerId} - Updating buttons. isConnected:`, isConnected);
187
+
154
188
  if (isConnected) {
155
- // Update to disconnect state (RED button)
156
- $button.classList.remove('btn-primary');
157
- $button.classList.add('btn-outline-danger');
158
- $buttonText.textContent = 'Disconnect';
159
- $action.value = 'disconnect';
160
-
161
- // Replace icon
162
- const $icon = $button.querySelector('.fa-icon');
163
- if ($icon) {
164
- $icon.classList.remove('fa-link');
165
- $icon.classList.add('fa-unlink');
166
- }
189
+ // Hide connect button, show disconnect button
190
+ $connectButton.classList.add('d-none');
191
+ $disconnectButton.classList.remove('d-none');
192
+ console.log(`[DEBUG] ${providerId} - Showing disconnect button`);
167
193
  } else {
168
- // Update to connect state (BLUE button)
169
- $button.classList.remove('btn-outline-danger');
170
- $button.classList.add('btn-primary');
171
- $buttonText.textContent = 'Connect';
172
- $action.value = 'connect';
173
-
174
- // Replace icon
175
- const $icon = $button.querySelector('.fa-icon');
176
- if ($icon) {
177
- $icon.classList.remove('fa-unlink');
178
- $icon.classList.add('fa-link');
179
- }
194
+ // Show connect button, hide disconnect button
195
+ $connectButton.classList.remove('d-none');
196
+ $disconnectButton.classList.add('d-none');
197
+ console.log(`[DEBUG] ${providerId} - Showing connect button`);
180
198
  }
181
199
  }
182
200
  }
@@ -185,12 +203,13 @@ function updateProviderStatus(providerId, userConnection, providerSettings) {
185
203
  function getConnectionDisplayName(connection) {
186
204
  if (!connection || !connection.identity) return 'Unknown';
187
205
 
188
- return connection.identity.global_name
189
- || connection.identity.username
190
- || connection.identity.name
191
- || connection.identity.email
192
- || connection.identity.id
193
- || 'Connected';
206
+ // 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';
194
213
  }
195
214
 
196
215
  // Initialize FormManager for a provider
@@ -205,9 +224,12 @@ function initializeProviderForm(providerId) {
205
224
 
206
225
  // Skip if already initialized
207
226
  if (connectionForms.has(providerId)) {
227
+ console.log(`FormManager already initialized for ${providerId}`);
208
228
  return;
209
229
  }
210
230
 
231
+ console.log(`Initializing FormManager for ${providerId}`);
232
+
211
233
  // Create new FormManager
212
234
  const formManager = new FormManager(`#${formId}`, {
213
235
  autoDisable: true,
@@ -217,14 +239,32 @@ function initializeProviderForm(providerId) {
217
239
  // Store the FormManager instance
218
240
  connectionForms.set(providerId, formManager);
219
241
 
242
+ // 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);
246
+
247
+ // When FormManager transitions to ready, update the button status
248
+ if (status === 'ready') {
249
+ console.log(`[DEBUG] ${providerId} - FormManager is ready, updating button status`);
250
+ const userConnection = accountData?.oauth2?.[providerId];
251
+ const providerSettings = appData?.oauth2?.[providerId];
252
+ updateProviderStatus(providerId, userConnection, providerSettings);
253
+ }
254
+ });
255
+
220
256
  // Handle form submission
221
257
  formManager.addEventListener('submit', async (event) => {
222
258
  event.preventDefault();
223
259
 
224
- const { data } = event.detail;
225
- const action = data.action;
260
+ const { data, submitButton } = event.detail;
226
261
  const provider = data.provider;
227
262
 
263
+ // Determine action from the clicked button's data-action attribute
264
+ const action = submitButton?.getAttribute('data-action');
265
+
266
+ console.log(`[DEBUG] ${providerId} - Form submitted. Action:`, action, 'Provider:', provider);
267
+
228
268
  try {
229
269
  if (action === 'connect') {
230
270
  await handleConnect(provider);
@@ -233,10 +273,13 @@ function initializeProviderForm(providerId) {
233
273
  if (success) {
234
274
  // Reset form state and update UI after successful disconnect
235
275
  formManager.setFormState('ready');
276
+ // Get provider settings to pass for description display
236
277
  const providerSettings = appData?.oauth2?.[provider];
237
278
  updateProviderStatus(provider, null, providerSettings);
238
279
  }
239
280
  }
281
+
282
+ // Success - FormManager will handle state automatically
240
283
  } catch (error) {
241
284
  // Show error and reset form state
242
285
  formManager.showError(error);
@@ -252,19 +295,15 @@ async function handleConnect(providerId) {
252
295
  throw new Error('This connection service is not available.');
253
296
  }
254
297
 
255
- // Get user token
256
- const token = await webManager.auth().getIdToken();
257
-
258
298
  // Get scope from provider settings (pass as array)
259
299
  const scope = provider.scope || [];
260
300
 
261
- const response = await fetch(getApiUrl(), {
301
+ const response = await authorizedFetch(getApiUrl(), {
262
302
  method: 'POST',
263
303
  timeout: 30000,
264
304
  response: 'json',
265
305
  tries: 2,
266
306
  body: {
267
- authenticationToken: token,
268
307
  command: 'user:oauth2',
269
308
  payload: {
270
309
  redirect: false,
@@ -276,6 +315,8 @@ async function handleConnect(providerId) {
276
315
  },
277
316
  });
278
317
 
318
+ console.log('OAuth connect response:', response);
319
+
279
320
  // For authorize requests, server returns an object with URL to redirect to
280
321
  if (response.url) {
281
322
  window.location.href = response.url;
@@ -296,20 +337,16 @@ async function handleDisconnect(providerId) {
296
337
  throw new Error('Disconnection cancelled');
297
338
  }
298
339
 
299
- // Get user token
300
- const token = await webManager.auth().getIdToken();
301
-
302
340
  // Get provider settings for scope (pass as array)
303
341
  const provider = appData?.oauth2?.[providerId] || {};
304
342
  const scope = provider.scope || [];
305
343
 
306
- const response = await fetch(getApiUrl(), {
344
+ const response = await authorizedFetch(getApiUrl(), {
307
345
  method: 'POST',
308
346
  timeout: 30000,
309
347
  response: 'json',
310
348
  tries: 2,
311
349
  body: {
312
- authenticationToken: token,
313
350
  command: 'user:oauth2',
314
351
  payload: {
315
352
  redirect: false,
@@ -321,12 +358,15 @@ async function handleDisconnect(providerId) {
321
358
  },
322
359
  });
323
360
 
361
+ console.log('OAuth disconnect response:', response);
362
+
324
363
  if (response.success) {
325
364
  // Update local account data
326
365
  if (accountData.oauth2 && accountData.oauth2[providerId]) {
327
366
  delete accountData.oauth2[providerId];
328
367
  }
329
368
 
369
+ // Return success
330
370
  return true;
331
371
  } else {
332
372
  throw new Error(response.message || 'Failed to disconnect');
@@ -335,6 +375,8 @@ async function handleDisconnect(providerId) {
335
375
 
336
376
  // Called when section is shown
337
377
  export function onShow() {
338
- // Don't re-run displayConnections() - it's already been called from loadData()
339
- // Re-running it would re-initialize FormManager and cause race conditions
378
+ // Refresh connections display when section is shown
379
+ if (accountData && appData) {
380
+ displayConnections();
381
+ }
340
382
  }
@@ -11,10 +11,10 @@ export function init(wm) {
11
11
  // Load referrals data
12
12
  export function loadData(account) {
13
13
  if (!account) return;
14
-
14
+
15
15
  // Update referral code (real code only)
16
16
  updateReferralCode(account.affiliate?.code);
17
-
17
+
18
18
  // Update referrals list
19
19
  updateReferralsList(account.affiliate?.referrals);
20
20
  }
@@ -22,7 +22,7 @@ export function loadData(account) {
22
22
  // Update referral code display
23
23
  function updateReferralCode(code) {
24
24
  const $codeInput = document.getElementById('referral-code-input');
25
-
25
+
26
26
  if ($codeInput) {
27
27
  if (code) {
28
28
  const baseUrl = window.location.origin;
@@ -39,19 +39,19 @@ function updateReferralsList(referrals) {
39
39
  const $recentReferrals = document.getElementById('recent-referrals');
40
40
  const $referralsBadge = document.getElementById('referrals-badge');
41
41
  const $referralsList = document.getElementById('referrals-list');
42
-
42
+
43
43
  // Initialize referrals array
44
44
  let referralData = referrals || [];
45
-
46
- // Add fake data if _test_prefill=true is in query string
45
+
46
+ // Add fake data if _dev_prefill=true is in query string
47
47
  const urlParams = new URLSearchParams(window.location.search);
48
- if (urlParams.get('_test_prefill') === 'true') {
48
+ if (urlParams.get('_dev_prefill') === 'true') {
49
49
  console.log('Adding fake referral data for testing');
50
50
  const fakeReferrals = generateFakeReferrals();
51
51
  // Add fake referrals to existing data
52
52
  referralData = [...referralData, ...fakeReferrals];
53
53
  }
54
-
54
+
55
55
  // Handle empty state
56
56
  if (!referralData || !Array.isArray(referralData) || referralData.length === 0) {
57
57
  // No referrals - show empty state
@@ -67,7 +67,7 @@ function updateReferralsList(referrals) {
67
67
  }
68
68
  return;
69
69
  }
70
-
70
+
71
71
  // Calculate stats
72
72
  const totalCount = referralData.length;
73
73
  const now = new Date();
@@ -76,19 +76,19 @@ function updateReferralsList(referrals) {
76
76
  const timestamp = ref.timestamp || ref.timestampUNIX * 1000;
77
77
  return timestamp >= thisMonth;
78
78
  }).length;
79
-
79
+
80
80
  // Update stats
81
81
  if ($totalReferrals) $totalReferrals.textContent = totalCount.toString();
82
82
  if ($recentReferrals) $recentReferrals.textContent = recentCount.toString();
83
83
  if ($referralsBadge) $referralsBadge.textContent = totalCount.toString();
84
-
84
+
85
85
  // Sort referrals by timestamp in reverse order (newest first)
86
86
  const sortedReferrals = [...referralData].sort((a, b) => {
87
87
  const timeA = a.timestamp || (a.timestampUNIX * 1000) || 0;
88
88
  const timeB = b.timestamp || (b.timestampUNIX * 1000) || 0;
89
89
  return timeB - timeA; // Reverse order
90
90
  });
91
-
91
+
92
92
  // Generate referral list HTML
93
93
  if ($referralsList) {
94
94
  if (sortedReferrals.length === 0) {
@@ -103,7 +103,7 @@ function updateReferralsList(referrals) {
103
103
  const date = timestamp ? new Date(timestamp) : null;
104
104
  const dateStr = date ? formatDate(date) : 'Unknown date';
105
105
  const timeStr = date ? formatTime(date) : '';
106
-
106
+
107
107
  return `
108
108
  <div class="list-group-item px-0">
109
109
  <div class="d-flex justify-content-between align-items-center">
@@ -123,7 +123,7 @@ function updateReferralsList(referrals) {
123
123
  </div>
124
124
  `;
125
125
  }).join('');
126
-
126
+
127
127
  $referralsList.innerHTML = referralHTML;
128
128
  }
129
129
  }
@@ -149,45 +149,45 @@ function formatTime(date) {
149
149
  // Get time since string
150
150
  function getTimeSince(timestamp) {
151
151
  if (!timestamp) return '<small class="text-muted">Unknown</small>';
152
-
152
+
153
153
  const now = Date.now();
154
154
  const diff = now - timestamp;
155
-
155
+
156
156
  // Less than 1 minute
157
157
  if (diff < 60000) {
158
158
  return '<small class="text-success">Just now</small>';
159
159
  }
160
-
160
+
161
161
  // Less than 1 hour
162
162
  if (diff < 3600000) {
163
163
  const minutes = Math.floor(diff / 60000);
164
164
  return `<small class="text-muted">${minutes} min${minutes > 1 ? 's' : ''} ago</small>`;
165
165
  }
166
-
166
+
167
167
  // Less than 24 hours
168
168
  if (diff < 86400000) {
169
169
  const hours = Math.floor(diff / 3600000);
170
170
  return `<small class="text-muted">${hours} hour${hours > 1 ? 's' : ''} ago</small>`;
171
171
  }
172
-
172
+
173
173
  // Less than 7 days
174
174
  if (diff < 604800000) {
175
175
  const days = Math.floor(diff / 86400000);
176
176
  return `<small class="text-muted">${days} day${days > 1 ? 's' : ''} ago</small>`;
177
177
  }
178
-
178
+
179
179
  // Less than 30 days
180
180
  if (diff < 2592000000) {
181
181
  const weeks = Math.floor(diff / 604800000);
182
182
  return `<small class="text-muted">${weeks} week${weeks > 1 ? 's' : ''} ago</small>`;
183
183
  }
184
-
184
+
185
185
  // More than 30 days
186
186
  const months = Math.floor(diff / 2592000000);
187
187
  if (months < 12) {
188
188
  return `<small class="text-muted">${months} month${months > 1 ? 's' : ''} ago</small>`;
189
189
  }
190
-
190
+
191
191
  const years = Math.floor(months / 12);
192
192
  return `<small class="text-muted">${years} year${years > 1 ? 's' : ''} ago</small>`;
193
193
  }
@@ -205,31 +205,31 @@ function setupButtons() {
205
205
  async function handleCopyReferralCode() {
206
206
  const $codeInput = document.getElementById('referral-code-input');
207
207
  const $copyBtn = document.getElementById('copy-referral-code-btn');
208
-
208
+
209
209
  if (!$codeInput || !$codeInput.value || $codeInput.value === 'No referral link available') {
210
210
  webManager.utilities().showNotification('No referral link to copy', 'warning');
211
211
  return;
212
212
  }
213
-
213
+
214
214
  try {
215
215
  // Copy the full URL directly from the input (it now contains the full URL)
216
216
  await webManager.utilities().clipboardCopy($codeInput);
217
-
217
+
218
218
  // Update button text temporarily
219
219
  const $text = $copyBtn.querySelector('.button-text');
220
220
  const originalText = $text.textContent;
221
-
221
+
222
222
  $text.textContent = 'Copied!';
223
223
  $copyBtn.classList.remove('btn-primary');
224
224
  $copyBtn.classList.add('btn-success');
225
-
225
+
226
226
  // Reset after 2 seconds
227
227
  setTimeout(() => {
228
228
  $text.textContent = originalText;
229
229
  $copyBtn.classList.remove('btn-success');
230
230
  $copyBtn.classList.add('btn-primary');
231
231
  }, 2000);
232
-
232
+
233
233
  } catch (err) {
234
234
  console.error('Failed to copy referral link:', err);
235
235
  webManager.utilities().showNotification('Failed to copy referral link', 'danger');
@@ -240,7 +240,7 @@ async function handleCopyReferralCode() {
240
240
  function generateFakeReferrals() {
241
241
  const now = Date.now();
242
242
  const oneDay = 24 * 60 * 60 * 1000;
243
-
243
+
244
244
  return [
245
245
  {
246
246
  uid: 'user_k9m2n8p4q1r7',