ultimate-jekyll-manager 0.0.116 → 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.
- package/CLAUDE.md +330 -138
- package/README.md +108 -0
- package/TODO.md +1 -1
- package/dist/assets/js/core/auth.js +3 -7
- package/dist/assets/js/libs/prerendered-icons.js +27 -0
- package/dist/assets/js/pages/account/index.js +1 -1
- package/dist/assets/js/pages/account/sections/api-keys.js +2 -6
- package/dist/assets/js/pages/account/sections/connections.js +91 -57
- package/dist/assets/js/pages/account/sections/referrals.js +29 -29
- package/dist/assets/js/pages/account/sections/security.js +95 -70
- package/dist/assets/js/pages/payment/checkout/index.js +6 -6
- package/dist/assets/js/pages/payment/checkout/modules/processors-main.js +2 -2
- package/dist/assets/js/pages/payment/checkout/modules/session.js +4 -4
- package/dist/defaults/dist/_includes/core/body.html +23 -0
- package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/account/index.html +42 -15
- package/dist/defaults/dist/pages/pricing.md +7 -0
- package/dist/service-worker.js +129 -139
- package/firebase-debug.log +350 -0
- package/package.json +10 -10
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
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
|
|
197
|
-
const response = await
|
|
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('
|
|
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
|
|
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
|
|
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
|
|
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,33 +54,51 @@ function displayConnections() {
|
|
|
51
54
|
const availableProviders = appData?.oauth2 || {};
|
|
52
55
|
const userConnections = accountData?.oauth2 || {};
|
|
53
56
|
|
|
54
|
-
console.log('Available OAuth providers:', availableProviders);
|
|
55
|
-
console.log('User connections:', userConnections);
|
|
57
|
+
console.log('[DEBUG] Available OAuth providers:', availableProviders);
|
|
58
|
+
console.log('[DEBUG] User connections:', userConnections);
|
|
56
59
|
|
|
57
60
|
// Check if any providers are configured
|
|
58
61
|
let hasEnabledProviders = false;
|
|
59
62
|
|
|
60
63
|
// Process each supported provider
|
|
61
64
|
supportedProviders.forEach(providerId => {
|
|
65
|
+
console.log(`[DEBUG] Processing provider: ${providerId}`);
|
|
66
|
+
|
|
62
67
|
const providerSettings = availableProviders[providerId];
|
|
63
68
|
const $providerElement = document.getElementById(`connection-${providerId}`);
|
|
64
69
|
|
|
65
|
-
|
|
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
|
+
|
|
74
|
+
if (!$providerElement) {
|
|
75
|
+
console.warn(`[DEBUG] ${providerId} - Provider element not found in DOM`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
66
78
|
|
|
67
79
|
// Check if provider is enabled
|
|
68
|
-
|
|
80
|
+
const isEnabled = providerSettings && providerSettings.enabled !== false;
|
|
81
|
+
console.log(`[DEBUG] ${providerId} - isEnabled:`, isEnabled);
|
|
82
|
+
|
|
83
|
+
if (isEnabled) {
|
|
69
84
|
hasEnabledProviders = true;
|
|
70
85
|
|
|
71
|
-
// Show the provider element
|
|
86
|
+
// Show the provider element first
|
|
87
|
+
console.log(`[DEBUG] ${providerId} - Removing d-none class`);
|
|
72
88
|
$providerElement.classList.remove('d-none');
|
|
73
89
|
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// Initialize FormManager for this provider
|
|
90
|
+
// Initialize FormManager for this provider before updating status
|
|
91
|
+
console.log(`[DEBUG] ${providerId} - About to initialize FormManager`);
|
|
78
92
|
initializeProviderForm(providerId);
|
|
93
|
+
|
|
94
|
+
// Update provider status based on user connection
|
|
95
|
+
const userConnection = userConnections[providerId];
|
|
96
|
+
console.log(`[DEBUG] ${providerId} - userConnection:`, userConnection);
|
|
97
|
+
console.log(`[DEBUG] ${providerId} - About to update provider status`);
|
|
98
|
+
updateProviderStatus(providerId, userConnection, providerSettings);
|
|
79
99
|
} else {
|
|
80
100
|
// Hide disabled providers
|
|
101
|
+
console.log(`[DEBUG] ${providerId} - Provider disabled, adding d-none class`);
|
|
81
102
|
$providerElement.classList.add('d-none');
|
|
82
103
|
}
|
|
83
104
|
});
|
|
@@ -95,14 +116,28 @@ function displayConnections() {
|
|
|
95
116
|
|
|
96
117
|
// Update provider status display
|
|
97
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
|
+
|
|
98
125
|
const $status = document.getElementById(`${providerId}-connection-status`);
|
|
99
126
|
const $description = document.getElementById(`${providerId}-connection-description`);
|
|
100
127
|
const $form = document.getElementById(`connection-form-${providerId}`);
|
|
101
|
-
const $
|
|
102
|
-
const $
|
|
103
|
-
|
|
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
|
+
});
|
|
104
138
|
|
|
105
139
|
const isConnected = userConnection && userConnection.identity;
|
|
140
|
+
console.log(`[DEBUG] ${providerId} - isConnected:`, isConnected);
|
|
106
141
|
|
|
107
142
|
// Always show description on the left
|
|
108
143
|
if ($description) {
|
|
@@ -113,7 +148,7 @@ function updateProviderStatus(providerId, userConnection, providerSettings) {
|
|
|
113
148
|
twitter: 'Share updates and connect with Twitter',
|
|
114
149
|
facebook: 'Connect your Facebook account for social features'
|
|
115
150
|
};
|
|
116
|
-
|
|
151
|
+
|
|
117
152
|
// Use provider description or fallback to default
|
|
118
153
|
const descriptionText = providerSettings?.description || defaultDescriptions[providerId] || `Connect your ${providerId.charAt(0).toUpperCase() + providerId.slice(1)} account`;
|
|
119
154
|
$description.textContent = descriptionText;
|
|
@@ -140,45 +175,28 @@ function updateProviderStatus(providerId, userConnection, providerSettings) {
|
|
|
140
175
|
$status.textContent = statusText;
|
|
141
176
|
$status.classList.remove('d-none');
|
|
142
177
|
} else {
|
|
143
|
-
// Not connected - hide status
|
|
178
|
+
// Not connected - hide status text
|
|
144
179
|
$status.textContent = '';
|
|
145
180
|
$status.classList.add('d-none');
|
|
146
181
|
}
|
|
147
182
|
}
|
|
148
183
|
|
|
149
|
-
|
|
184
|
+
// Show/hide appropriate button
|
|
185
|
+
if ($connectButton && $disconnectButton) {
|
|
186
|
+
console.log(`[DEBUG] ${providerId} - Updating buttons. isConnected:`, isConnected);
|
|
187
|
+
|
|
150
188
|
if (isConnected) {
|
|
151
|
-
//
|
|
152
|
-
$
|
|
153
|
-
$
|
|
154
|
-
|
|
155
|
-
$action.value = 'disconnect';
|
|
156
|
-
|
|
157
|
-
// Replace icon - need to update the button's first icon element
|
|
158
|
-
const $icon = $button.querySelector('.fa-icon');
|
|
159
|
-
if ($icon) {
|
|
160
|
-
// Change icon classes from link to unlink
|
|
161
|
-
$icon.classList.remove('fa-link');
|
|
162
|
-
$icon.classList.add('fa-unlink');
|
|
163
|
-
}
|
|
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`);
|
|
164
193
|
} else {
|
|
165
|
-
//
|
|
166
|
-
$
|
|
167
|
-
$
|
|
168
|
-
|
|
169
|
-
$action.value = 'connect';
|
|
170
|
-
|
|
171
|
-
// Replace icon
|
|
172
|
-
const $icon = $button.querySelector('.fa-icon');
|
|
173
|
-
if ($icon) {
|
|
174
|
-
// Change icon classes from unlink to link
|
|
175
|
-
$icon.classList.remove('fa-unlink');
|
|
176
|
-
$icon.classList.add('fa-link');
|
|
177
|
-
}
|
|
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`);
|
|
178
198
|
}
|
|
179
199
|
}
|
|
180
|
-
|
|
181
|
-
// The updated section has been removed from HTML, no longer needed
|
|
182
200
|
}
|
|
183
201
|
|
|
184
202
|
// Get display name for connection
|
|
@@ -199,13 +217,19 @@ function initializeProviderForm(providerId) {
|
|
|
199
217
|
const formId = `connection-form-${providerId}`;
|
|
200
218
|
const form = document.getElementById(formId);
|
|
201
219
|
|
|
202
|
-
if (!form)
|
|
220
|
+
if (!form) {
|
|
221
|
+
console.warn(`Form not found for provider: ${providerId}`);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
203
224
|
|
|
204
225
|
// Skip if already initialized
|
|
205
226
|
if (connectionForms.has(providerId)) {
|
|
227
|
+
console.log(`FormManager already initialized for ${providerId}`);
|
|
206
228
|
return;
|
|
207
229
|
}
|
|
208
230
|
|
|
231
|
+
console.log(`Initializing FormManager for ${providerId}`);
|
|
232
|
+
|
|
209
233
|
// Create new FormManager
|
|
210
234
|
const formManager = new FormManager(`#${formId}`, {
|
|
211
235
|
autoDisable: true,
|
|
@@ -215,14 +239,32 @@ function initializeProviderForm(providerId) {
|
|
|
215
239
|
// Store the FormManager instance
|
|
216
240
|
connectionForms.set(providerId, formManager);
|
|
217
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
|
+
|
|
218
256
|
// Handle form submission
|
|
219
257
|
formManager.addEventListener('submit', async (event) => {
|
|
220
258
|
event.preventDefault();
|
|
221
259
|
|
|
222
|
-
const { data } = event.detail;
|
|
223
|
-
const action = data.action;
|
|
260
|
+
const { data, submitButton } = event.detail;
|
|
224
261
|
const provider = data.provider;
|
|
225
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
|
+
|
|
226
268
|
try {
|
|
227
269
|
if (action === 'connect') {
|
|
228
270
|
await handleConnect(provider);
|
|
@@ -253,19 +295,15 @@ async function handleConnect(providerId) {
|
|
|
253
295
|
throw new Error('This connection service is not available.');
|
|
254
296
|
}
|
|
255
297
|
|
|
256
|
-
// Get user token
|
|
257
|
-
const token = await webManager.auth().getIdToken();
|
|
258
|
-
|
|
259
298
|
// Get scope from provider settings (pass as array)
|
|
260
299
|
const scope = provider.scope || [];
|
|
261
300
|
|
|
262
|
-
const response = await
|
|
301
|
+
const response = await authorizedFetch(getApiUrl(), {
|
|
263
302
|
method: 'POST',
|
|
264
303
|
timeout: 30000,
|
|
265
304
|
response: 'json',
|
|
266
305
|
tries: 2,
|
|
267
306
|
body: {
|
|
268
|
-
authenticationToken: token,
|
|
269
307
|
command: 'user:oauth2',
|
|
270
308
|
payload: {
|
|
271
309
|
redirect: false,
|
|
@@ -299,20 +337,16 @@ async function handleDisconnect(providerId) {
|
|
|
299
337
|
throw new Error('Disconnection cancelled');
|
|
300
338
|
}
|
|
301
339
|
|
|
302
|
-
// Get user token
|
|
303
|
-
const token = await webManager.auth().getIdToken();
|
|
304
|
-
|
|
305
340
|
// Get provider settings for scope (pass as array)
|
|
306
341
|
const provider = appData?.oauth2?.[providerId] || {};
|
|
307
342
|
const scope = provider.scope || [];
|
|
308
343
|
|
|
309
|
-
const response = await
|
|
344
|
+
const response = await authorizedFetch(getApiUrl(), {
|
|
310
345
|
method: 'POST',
|
|
311
346
|
timeout: 30000,
|
|
312
347
|
response: 'json',
|
|
313
348
|
tries: 2,
|
|
314
349
|
body: {
|
|
315
|
-
authenticationToken: token,
|
|
316
350
|
command: 'user:oauth2',
|
|
317
351
|
payload: {
|
|
318
352
|
redirect: false,
|
|
@@ -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
|
|
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('
|
|
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',
|