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,6 +1,7 @@
|
|
|
1
1
|
// Security section module
|
|
2
|
-
import
|
|
2
|
+
import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';
|
|
3
3
|
import { FormManager } from '__main_assets__/js/libs/form-manager.js';
|
|
4
|
+
import { getPrerenderedIcon } from '__main_assets__/js/libs/prerendered-icons.js';
|
|
4
5
|
|
|
5
6
|
let webManager = null;
|
|
6
7
|
let firebaseAuth = null;
|
|
@@ -11,7 +12,6 @@ let signoutAllForm = null; // FormManager instance for sign out all sessions
|
|
|
11
12
|
export function init(wm) {
|
|
12
13
|
webManager = wm;
|
|
13
14
|
initializeSigninMethods();
|
|
14
|
-
initializeSigninMethodForms();
|
|
15
15
|
initializeSignoutAllForm();
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -19,9 +19,15 @@ export function init(wm) {
|
|
|
19
19
|
export function loadData(account) {
|
|
20
20
|
if (!account) return;
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
console.log('[DEBUG] security.js - loadData() called with account:', account);
|
|
23
|
+
|
|
24
|
+
// CRITICAL: Update signin methods BEFORE initializing FormManagers
|
|
25
|
+
// This ensures FormManager stores the correct button state from the start
|
|
23
26
|
updateSigninMethods();
|
|
24
27
|
|
|
28
|
+
// Initialize FormManagers AFTER setting correct button states
|
|
29
|
+
initializeSigninMethodForms();
|
|
30
|
+
|
|
25
31
|
// Update 2FA status
|
|
26
32
|
update2FAStatus(account.security?.twoFactor);
|
|
27
33
|
|
|
@@ -31,6 +37,8 @@ export function loadData(account) {
|
|
|
31
37
|
|
|
32
38
|
// Initialize signin methods
|
|
33
39
|
async function initializeSigninMethods() {
|
|
40
|
+
console.log('[DEBUG] security.js - initializeSigninMethods() called');
|
|
41
|
+
|
|
34
42
|
// Get Firebase auth instance
|
|
35
43
|
firebaseAuth = webManager.firebaseAuth;
|
|
36
44
|
|
|
@@ -61,13 +69,23 @@ async function checkRedirectResult() {
|
|
|
61
69
|
|
|
62
70
|
// Update signin methods display
|
|
63
71
|
async function updateSigninMethods() {
|
|
72
|
+
console.log('[DEBUG] security.js - updateSigninMethods() called');
|
|
73
|
+
|
|
64
74
|
// Use Firebase auth directly for most up-to-date provider information
|
|
65
75
|
const firebaseUser = firebaseAuth?.currentUser;
|
|
66
|
-
if (!firebaseUser)
|
|
76
|
+
if (!firebaseUser) {
|
|
77
|
+
console.log('[DEBUG] security.js - No firebaseUser, returning');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
67
80
|
|
|
68
81
|
// Get the formatted user from webManager for consistency, but we'll use firebaseUser for provider data
|
|
69
82
|
const user = webManager.auth().getUser();
|
|
70
|
-
if (!user)
|
|
83
|
+
if (!user) {
|
|
84
|
+
console.log('[DEBUG] security.js - No user from webManager, returning');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console.log('[DEBUG] security.js - firebaseUser.providerData:', firebaseUser.providerData);
|
|
71
89
|
|
|
72
90
|
// Update password email display
|
|
73
91
|
const $passwordEmail = document.getElementById('password-email');
|
|
@@ -75,42 +93,43 @@ async function updateSigninMethods() {
|
|
|
75
93
|
// Check if user has password provider using firebaseUser for most up-to-date data
|
|
76
94
|
const hasPassword = firebaseUser.providerData?.some(provider => provider.providerId === 'password');
|
|
77
95
|
$passwordEmail.textContent = hasPassword ? user.email : 'Not set';
|
|
96
|
+
console.log('[DEBUG] security.js - hasPassword:', hasPassword);
|
|
78
97
|
}
|
|
79
98
|
|
|
80
99
|
// Update Google signin display
|
|
81
100
|
const $googleEmail = document.getElementById('google-email');
|
|
82
101
|
const $googleForm = document.getElementById('signin-method-google-form');
|
|
83
|
-
const $
|
|
84
|
-
const $
|
|
85
|
-
const $googleAction = $googleForm?.querySelector('input[name="action"]');
|
|
86
|
-
const $googleIcon = $googleBtn?.querySelector('.fa-icon');
|
|
102
|
+
const $connectButton = $googleForm?.querySelector('button[data-action="connect"]');
|
|
103
|
+
const $disconnectButton = $googleForm?.querySelector('button[data-action="disconnect"]');
|
|
87
104
|
|
|
88
|
-
|
|
105
|
+
console.log('[DEBUG] security.js - Google DOM elements:', {
|
|
106
|
+
$googleEmail: !!$googleEmail,
|
|
107
|
+
$googleForm: !!$googleForm,
|
|
108
|
+
$connectButton: !!$connectButton,
|
|
109
|
+
$disconnectButton: !!$disconnectButton
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
if ($googleEmail && $connectButton && $disconnectButton) {
|
|
89
113
|
// Check if user has Google provider using firebaseUser for most up-to-date data
|
|
90
114
|
const googleProvider = firebaseUser.providerData?.find(provider => provider.providerId === 'google.com');
|
|
91
115
|
|
|
116
|
+
console.log('[DEBUG] security.js - googleProvider:', googleProvider);
|
|
117
|
+
console.log('[DEBUG] security.js - googleProvider found:', !!googleProvider);
|
|
118
|
+
|
|
92
119
|
if (googleProvider) {
|
|
120
|
+
console.log('[DEBUG] security.js - Showing disconnect button');
|
|
121
|
+
|
|
93
122
|
$googleEmail.textContent = googleProvider.email || 'Connected';
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
$
|
|
97
|
-
$googleBtn.classList.add('btn-outline-danger');
|
|
98
|
-
// Update icon from link to unlink
|
|
99
|
-
if ($googleIcon) {
|
|
100
|
-
$googleIcon.classList.remove('fa-link');
|
|
101
|
-
$googleIcon.classList.add('fa-unlink');
|
|
102
|
-
}
|
|
123
|
+
// Hide connect button, show disconnect button
|
|
124
|
+
$connectButton.classList.add('d-none');
|
|
125
|
+
$disconnectButton.classList.remove('d-none');
|
|
103
126
|
} else {
|
|
127
|
+
console.log('[DEBUG] security.js - Showing connect button');
|
|
128
|
+
|
|
104
129
|
$googleEmail.textContent = 'Not connected';
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
$
|
|
108
|
-
$googleBtn.classList.add('btn-primary');
|
|
109
|
-
// Update icon from unlink to link
|
|
110
|
-
if ($googleIcon) {
|
|
111
|
-
$googleIcon.classList.remove('fa-unlink');
|
|
112
|
-
$googleIcon.classList.add('fa-link');
|
|
113
|
-
}
|
|
130
|
+
// Show connect button, hide disconnect button
|
|
131
|
+
$connectButton.classList.remove('d-none');
|
|
132
|
+
$disconnectButton.classList.add('d-none');
|
|
114
133
|
}
|
|
115
134
|
}
|
|
116
135
|
}
|
|
@@ -167,16 +186,14 @@ async function updateActiveSessions(account) {
|
|
|
167
186
|
|
|
168
187
|
// Fetch other active sessions from server
|
|
169
188
|
try {
|
|
170
|
-
const token = await webManager.auth().getIdToken();
|
|
171
189
|
const serverApiURL = webManager.getApiUrl() + '/backend-manager';
|
|
172
190
|
|
|
173
|
-
const data = await
|
|
191
|
+
const data = await authorizedFetch(serverApiURL, {
|
|
174
192
|
method: 'POST',
|
|
175
193
|
timeout: 60000,
|
|
176
194
|
response: 'json',
|
|
177
195
|
tries: 2,
|
|
178
196
|
body: {
|
|
179
|
-
authenticationToken: token,
|
|
180
197
|
command: 'user:get-active-sessions',
|
|
181
198
|
payload: {
|
|
182
199
|
// id: 'app',
|
|
@@ -184,14 +201,12 @@ async function updateActiveSessions(account) {
|
|
|
184
201
|
},
|
|
185
202
|
});
|
|
186
203
|
|
|
187
|
-
console.log('Active sessions data from server:', data);
|
|
188
|
-
|
|
189
204
|
// Process sessions from server response
|
|
190
205
|
let sessionData = data || {};
|
|
191
206
|
|
|
192
|
-
// Add fake data if
|
|
207
|
+
// Add fake data if _dev_prefill=true is in query string
|
|
193
208
|
const urlParams = new URLSearchParams(window.location.search);
|
|
194
|
-
if (urlParams.get('
|
|
209
|
+
if (urlParams.get('_dev_prefill') === 'true') {
|
|
195
210
|
console.log('Adding fake session data for testing');
|
|
196
211
|
const fakeSessions = generateFakeSessions();
|
|
197
212
|
// Merge fake sessions with existing data (fake sessions don't override real ones)
|
|
@@ -248,9 +263,9 @@ async function updateActiveSessions(account) {
|
|
|
248
263
|
};
|
|
249
264
|
|
|
250
265
|
// Only add if it's different from current session (different IP or timestamp)
|
|
251
|
-
if (!sessions[0]
|
|
252
|
-
(lastSession.ip !== sessions[0].ip
|
|
253
|
-
|
|
266
|
+
if (!sessions[0]
|
|
267
|
+
|| (lastSession.ip !== sessions[0].ip
|
|
268
|
+
|| lastSession.timestampUNIX !== sessions[0].timestampUNIX)) {
|
|
254
269
|
sessions.push(lastSession);
|
|
255
270
|
}
|
|
256
271
|
}
|
|
@@ -261,23 +276,24 @@ async function updateActiveSessions(account) {
|
|
|
261
276
|
return;
|
|
262
277
|
}
|
|
263
278
|
|
|
264
|
-
const sessionHTML = sessions.map(session => {
|
|
279
|
+
const sessionHTML = sessions.map((session, index) => {
|
|
265
280
|
const deviceName = session.device || 'Unknown Device';
|
|
266
281
|
const browserName = session.browser || 'Unknown Browser';
|
|
267
282
|
const location = formatSessionLocation(session);
|
|
283
|
+
const isLast = index === sessions.length - 1;
|
|
268
284
|
|
|
269
285
|
return `
|
|
270
|
-
<div class="
|
|
286
|
+
<div class="px-0 py-3${isLast ? '' : ' border-bottom'}">
|
|
271
287
|
<div class="d-flex justify-content-between align-items-start">
|
|
272
|
-
<div class="d-flex align-items-
|
|
273
|
-
<div class="me-3
|
|
288
|
+
<div class="d-flex align-items-center">
|
|
289
|
+
<div class="d-flex align-items-center justify-content-center me-3 flex-shrink-0 text-muted">
|
|
274
290
|
${getDeviceIcon(session.platform || deviceName)}
|
|
275
291
|
</div>
|
|
276
292
|
<div>
|
|
277
|
-
<
|
|
278
|
-
<
|
|
279
|
-
${location ? `<
|
|
280
|
-
${session.ip ? `<
|
|
293
|
+
<strong>${deviceName}</strong>
|
|
294
|
+
<div class="text-muted small">${browserName}${session.mobile !== undefined ? ` • ${session.mobile ? 'Mobile' : 'Desktop'}` : ''}</div>
|
|
295
|
+
${location ? `<div class="text-muted small">${location}</div>` : ''}
|
|
296
|
+
${session.ip ? `<div class="text-muted small">IP: ${session.ip}</div>` : ''}
|
|
281
297
|
</div>
|
|
282
298
|
</div>
|
|
283
299
|
<div class="text-end">
|
|
@@ -294,10 +310,14 @@ async function updateActiveSessions(account) {
|
|
|
294
310
|
|
|
295
311
|
// Initialize FormManager for signin methods
|
|
296
312
|
function initializeSigninMethodForms() {
|
|
313
|
+
console.log('[DEBUG] security.js - initializeSigninMethodForms() called');
|
|
314
|
+
|
|
297
315
|
// Initialize password form
|
|
298
316
|
const $passwordForm = document.getElementById('signin-method-password-form');
|
|
299
317
|
|
|
300
318
|
if ($passwordForm && !signinMethodForms.has('password')) {
|
|
319
|
+
console.log('[DEBUG] security.js - Initializing password FormManager');
|
|
320
|
+
|
|
301
321
|
const formManager = new FormManager($passwordForm, {
|
|
302
322
|
allowMultipleSubmissions: false,
|
|
303
323
|
autoDisable: true,
|
|
@@ -321,42 +341,43 @@ function initializeSigninMethodForms() {
|
|
|
321
341
|
const $googleForm = document.getElementById('signin-method-google-form');
|
|
322
342
|
|
|
323
343
|
if ($googleForm && !signinMethodForms.has('google')) {
|
|
344
|
+
console.log('[DEBUG] security.js - About to initialize Google FormManager');
|
|
345
|
+
console.log('[DEBUG] security.js - Google form exists:', !!$googleForm);
|
|
346
|
+
|
|
324
347
|
const formManager = new FormManager($googleForm, {
|
|
325
348
|
autoDisable: true,
|
|
326
349
|
showSpinner: true
|
|
327
350
|
});
|
|
328
351
|
|
|
329
352
|
signinMethodForms.set('google', formManager);
|
|
353
|
+
console.log('[DEBUG] security.js - Google FormManager initialized and stored');
|
|
330
354
|
|
|
331
355
|
formManager.addEventListener('submit', async (event) => {
|
|
332
356
|
event.preventDefault();
|
|
333
|
-
const {
|
|
357
|
+
const { submitButton } = event.detail;
|
|
358
|
+
|
|
359
|
+
// Determine action from the clicked button's data-action attribute
|
|
360
|
+
const action = submitButton?.getAttribute('data-action');
|
|
334
361
|
|
|
335
362
|
try {
|
|
336
|
-
if (
|
|
363
|
+
if (action === 'disconnect') {
|
|
337
364
|
await disconnectGoogleProvider();
|
|
338
|
-
} else {
|
|
365
|
+
} else if (action === 'connect') {
|
|
339
366
|
await connectGoogleProvider();
|
|
340
367
|
}
|
|
341
368
|
|
|
342
369
|
// Set form state back to ready first
|
|
343
370
|
formManager.setFormState('ready');
|
|
344
371
|
|
|
345
|
-
// Then update display (
|
|
346
|
-
|
|
347
|
-
setTimeout(() => {
|
|
348
|
-
updateSigninMethods();
|
|
349
|
-
}, 0);
|
|
372
|
+
// Then update display (this will set the button text correctly again)
|
|
373
|
+
updateSigninMethods();
|
|
350
374
|
} catch (error) {
|
|
351
375
|
// Reset form state
|
|
352
376
|
formManager.setFormState('ready');
|
|
353
377
|
|
|
354
378
|
// If user cancelled, also update the display to ensure button state is correct
|
|
355
379
|
if (error.message === 'Disconnection cancelled') {
|
|
356
|
-
|
|
357
|
-
setTimeout(() => {
|
|
358
|
-
updateSigninMethods();
|
|
359
|
-
}, 0);
|
|
380
|
+
updateSigninMethods();
|
|
360
381
|
} else {
|
|
361
382
|
// Show error for other failures
|
|
362
383
|
formManager.showError(error);
|
|
@@ -429,9 +450,9 @@ async function connectGoogleProvider() {
|
|
|
429
450
|
return result;
|
|
430
451
|
} catch (error) {
|
|
431
452
|
// Check if we should fallback to redirect
|
|
432
|
-
if (error.code === 'auth/popup-blocked'
|
|
433
|
-
error.code === 'auth/popup-closed-by-user'
|
|
434
|
-
error.code === 'auth/cancelled-popup-request') {
|
|
453
|
+
if (error.code === 'auth/popup-blocked'
|
|
454
|
+
|| error.code === 'auth/popup-closed-by-user'
|
|
455
|
+
|| error.code === 'auth/cancelled-popup-request') {
|
|
435
456
|
|
|
436
457
|
console.log('Popup failed, falling back to redirect:', error.code);
|
|
437
458
|
|
|
@@ -583,20 +604,25 @@ function getPlatformName(platform) {
|
|
|
583
604
|
// Get device icon based on device type
|
|
584
605
|
function getDeviceIcon(device) {
|
|
585
606
|
const deviceLower = (device || '').toLowerCase();
|
|
607
|
+
let iconName = 'desktop'; // default
|
|
586
608
|
|
|
587
|
-
if (deviceLower.includes('iphone')
|
|
588
|
-
|
|
609
|
+
if (deviceLower.includes('iphone')
|
|
610
|
+
|| deviceLower.includes('ipad')
|
|
611
|
+
|| deviceLower.includes('ios')
|
|
612
|
+
|| deviceLower.includes('mac')) {
|
|
613
|
+
iconName = 'apple';
|
|
589
614
|
} else if (deviceLower.includes('android')) {
|
|
590
|
-
|
|
615
|
+
iconName = 'android';
|
|
591
616
|
} else if (deviceLower.includes('windows')) {
|
|
592
|
-
|
|
617
|
+
iconName = 'windows';
|
|
593
618
|
} else if (deviceLower.includes('linux')) {
|
|
594
|
-
|
|
619
|
+
iconName = 'linux';
|
|
595
620
|
} else if (deviceLower.includes('chrome')) {
|
|
596
|
-
|
|
597
|
-
} else {
|
|
598
|
-
return '<i class="fa-solid fa-desktop fa-lg"></i>';
|
|
621
|
+
iconName = 'chrome';
|
|
599
622
|
}
|
|
623
|
+
|
|
624
|
+
// Get the pre-rendered icon
|
|
625
|
+
return getPrerenderedIcon(iconName);
|
|
600
626
|
}
|
|
601
627
|
|
|
602
628
|
// Format location from session data
|
|
@@ -703,4 +729,3 @@ function formatDate(timestamp) {
|
|
|
703
729
|
// More than 7 days - show full date
|
|
704
730
|
return date.toLocaleDateString() + ' at ' + date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
705
731
|
}
|
|
706
|
-
|
|
@@ -298,8 +298,8 @@ async function initializeCheckout() {
|
|
|
298
298
|
const urlParams = new URLSearchParams(window.location.search);
|
|
299
299
|
const productId = urlParams.get('product');
|
|
300
300
|
const frequency = urlParams.get('frequency') || 'annually';
|
|
301
|
-
const
|
|
302
|
-
const
|
|
301
|
+
const _dev_appId = urlParams.get('_dev_appId');
|
|
302
|
+
const _dev_trialEligible = urlParams.get('_dev_trialEligible');
|
|
303
303
|
|
|
304
304
|
// Product ID is required
|
|
305
305
|
if (!productId) {
|
|
@@ -307,7 +307,7 @@ async function initializeCheckout() {
|
|
|
307
307
|
}
|
|
308
308
|
|
|
309
309
|
// Check for testing parameters
|
|
310
|
-
const appId =
|
|
310
|
+
const appId = _dev_appId || webManager.config.brand.id;
|
|
311
311
|
|
|
312
312
|
// Warmup server (fire and forget)
|
|
313
313
|
warmupServer(webManager);
|
|
@@ -331,10 +331,10 @@ async function initializeCheckout() {
|
|
|
331
331
|
let trialEligibilityResult = trialEligible;
|
|
332
332
|
|
|
333
333
|
// Override trial eligibility for testing (only in development)
|
|
334
|
-
if (
|
|
335
|
-
if (
|
|
334
|
+
if (_dev_trialEligible && webManager.isDevelopment()) {
|
|
335
|
+
if (_dev_trialEligible === 'false') {
|
|
336
336
|
trialEligibilityResult = { status: 'fulfilled', value: false };
|
|
337
|
-
} else if (
|
|
337
|
+
} else if (_dev_trialEligible === 'true') {
|
|
338
338
|
trialEligibilityResult = { status: 'fulfilled', value: true };
|
|
339
339
|
}
|
|
340
340
|
}
|
|
@@ -41,9 +41,9 @@ export class PaymentProcessorManager {
|
|
|
41
41
|
|
|
42
42
|
// Determine processor based on payment method and available API keys
|
|
43
43
|
if (paymentMethod === 'card') {
|
|
44
|
-
// Check for
|
|
44
|
+
// Check for _dev_cardProcessor override in URL params (for testing)
|
|
45
45
|
const urlParams = new URLSearchParams(window.location.search);
|
|
46
|
-
const forcedProcessor = urlParams.get('
|
|
46
|
+
const forcedProcessor = urlParams.get('_dev_cardProcessor');
|
|
47
47
|
|
|
48
48
|
if (forcedProcessor && this.processors[forcedProcessor]) {
|
|
49
49
|
processorName = forcedProcessor;
|
|
@@ -30,7 +30,7 @@ export function buildPaymentIntentData(webManager) {
|
|
|
30
30
|
let processorName = state.paymentMethod;
|
|
31
31
|
if (state.paymentMethod === 'card') {
|
|
32
32
|
// Determine which processor will be used for card payments
|
|
33
|
-
const forcedProcessor = urlParams.get('
|
|
33
|
+
const forcedProcessor = urlParams.get('_dev_cardProcessor');
|
|
34
34
|
|
|
35
35
|
if (forcedProcessor) {
|
|
36
36
|
processorName = forcedProcessor;
|
|
@@ -46,7 +46,7 @@ export function buildPaymentIntentData(webManager) {
|
|
|
46
46
|
// Get UTM parameters from storage
|
|
47
47
|
const utmData = webManager.storage().get('marketing.utm');
|
|
48
48
|
let utm = {};
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
// Check if stored UTM data exists and is less than 30 days old
|
|
51
51
|
if (utmData && utmData.timestamp && utmData.tags) {
|
|
52
52
|
const daysDiff = (new Date() - new Date(utmData.timestamp)) / (1000 * 60 * 60 * 24);
|
|
@@ -56,8 +56,8 @@ export function buildPaymentIntentData(webManager) {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
// Check for test app ID override
|
|
59
|
-
const
|
|
60
|
-
const appId =
|
|
59
|
+
const _dev_appId = urlParams.get('_dev_appId');
|
|
60
|
+
const appId = _dev_appId || webManager.config.brand.id;
|
|
61
61
|
|
|
62
62
|
// Build the payment intent data structure
|
|
63
63
|
const paymentIntentData = {
|
|
@@ -35,6 +35,29 @@
|
|
|
35
35
|
</div>
|
|
36
36
|
</div>
|
|
37
37
|
|
|
38
|
+
{%- comment -%}
|
|
39
|
+
Icon Pre-rendering System
|
|
40
|
+
Pages can specify which icons to pre-render in their frontmatter:
|
|
41
|
+
prerender_icons:
|
|
42
|
+
- name: "apple"
|
|
43
|
+
class: "fa-3xl"
|
|
44
|
+
- name: "android"
|
|
45
|
+
class: "fa-2xl"
|
|
46
|
+
{%- endcomment -%}
|
|
47
|
+
{%- assign icons = page.resolved.prerender_icons | default: empty -%}
|
|
48
|
+
{%- iftruthy icons -%}
|
|
49
|
+
<!-- Pre-rendered Icon Templates -->
|
|
50
|
+
<div id="prerendered-icons" class="d-none" aria-hidden="true">
|
|
51
|
+
{%- for icon in icons -%}
|
|
52
|
+
{%- assign icon_name = icon.name | default: icon -%}
|
|
53
|
+
{%- assign icon_class = icon.class | default: "fa-3xl" -%}
|
|
54
|
+
<div data-icon="{{ icon_name }}" data-class="{{ icon_class }}">
|
|
55
|
+
{% uj_icon icon_name, icon_class %}
|
|
56
|
+
</div>
|
|
57
|
+
{%- endfor -%}
|
|
58
|
+
</div>
|
|
59
|
+
{%- endiftruthy -%}
|
|
60
|
+
|
|
38
61
|
<!-- Script to prevent clicks on disabled elements during page load -->
|
|
39
62
|
<script type="text/javascript">
|
|
40
63
|
(function() {
|
|
@@ -13,6 +13,21 @@ web_manager:
|
|
|
13
13
|
config:
|
|
14
14
|
policy: "authenticated"
|
|
15
15
|
|
|
16
|
+
### ICON PRE-RENDERING ###
|
|
17
|
+
prerender_icons:
|
|
18
|
+
- name: "apple"
|
|
19
|
+
class: "fa-3xl"
|
|
20
|
+
- name: "android"
|
|
21
|
+
class: "fa-3xl"
|
|
22
|
+
- name: "windows"
|
|
23
|
+
class: "fa-3xl"
|
|
24
|
+
- name: "linux"
|
|
25
|
+
class: "fa-3xl"
|
|
26
|
+
- name: "chrome"
|
|
27
|
+
class: "fa-3xl"
|
|
28
|
+
- name: "desktop"
|
|
29
|
+
class: "fa-3xl"
|
|
30
|
+
|
|
16
31
|
### PAGE CONFIG ###
|
|
17
32
|
sections:
|
|
18
33
|
- id: "profile"
|
|
@@ -790,20 +805,24 @@ badges:
|
|
|
790
805
|
</div>
|
|
791
806
|
</div>
|
|
792
807
|
<div class="flex-shrink-0">
|
|
793
|
-
<form id="signin-method-{{ method.id }}-form" class="d-inline" novalidate>
|
|
808
|
+
<form id="signin-method-{{ method.id }}-form" class="d-grid d-sm-inline-block" novalidate>
|
|
794
809
|
<input type="hidden" name="method" value="{{ method.id }}">
|
|
795
810
|
{% if method.id == "password" %}
|
|
796
|
-
<
|
|
797
|
-
<button type="submit" class="btn btn-primary btn-sm w-100 w-sm-auto">
|
|
811
|
+
<button type="submit" class="btn btn-primary btn-sm" data-action="change">
|
|
798
812
|
{% uj_icon "key", "me-1" %}
|
|
799
813
|
<span class="button-text">Change</span>
|
|
800
814
|
</button>
|
|
801
815
|
{% else %}
|
|
802
|
-
|
|
803
|
-
<button type="submit" class="btn btn-primary btn-sm
|
|
816
|
+
<!-- Connect button -->
|
|
817
|
+
<button type="submit" class="btn btn-primary btn-sm" data-action="connect">
|
|
804
818
|
{% uj_icon "link", "me-1" %}
|
|
805
819
|
<span class="button-text">Connect</span>
|
|
806
820
|
</button>
|
|
821
|
+
<!-- Disconnect button (hidden by default) -->
|
|
822
|
+
<button type="submit" class="btn btn-sm btn-outline-danger d-none" data-action="disconnect">
|
|
823
|
+
{% uj_icon "unlink", "me-1" %}
|
|
824
|
+
<span class="button-text">Disconnect</span>
|
|
825
|
+
</button>
|
|
807
826
|
{% endif %}
|
|
808
827
|
</form>
|
|
809
828
|
</div>
|
|
@@ -828,6 +847,7 @@ badges:
|
|
|
828
847
|
<div class="card-body">
|
|
829
848
|
<h5 class="card-title">Active Sessions</h5>
|
|
830
849
|
<p class="card-text text-muted">Manage your active sessions across devices. Review and revoke access from unrecognized devices.</p>
|
|
850
|
+
|
|
831
851
|
<div id="active-sessions-list" class="list-group list-group-flush mb-3">
|
|
832
852
|
<!-- Sessions will be loaded here -->
|
|
833
853
|
<div class="text-center py-3">
|
|
@@ -872,7 +892,7 @@ badges:
|
|
|
872
892
|
</div>
|
|
873
893
|
|
|
874
894
|
{% for connection in page.resolved.connections %}
|
|
875
|
-
<div id="connection-{{ connection.id }}" class="
|
|
895
|
+
<div id="connection-{{ connection.id }}" class="{% unless forloop.first %}border-top{% endunless %} px-0 py-3 d-none">
|
|
876
896
|
<div class="d-flex flex-column flex-sm-row align-items-stretch align-items-sm-center justify-content-between gap-3">
|
|
877
897
|
<div class="d-flex align-items-center">
|
|
878
898
|
<div class="d-flex align-items-center justify-content-center me-3 flex-shrink-0 fa fa-3xl">
|
|
@@ -884,15 +904,22 @@ badges:
|
|
|
884
904
|
</div>
|
|
885
905
|
</div>
|
|
886
906
|
<div class="text-start text-sm-end flex-shrink-0">
|
|
887
|
-
<
|
|
888
|
-
<
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
907
|
+
<div>
|
|
908
|
+
<form id="connection-form-{{ connection.id }}" class="d-grid d-sm-inline-block" novalidate>
|
|
909
|
+
<input type="hidden" name="provider" value="{{ connection.id }}">
|
|
910
|
+
<!-- Connect button -->
|
|
911
|
+
<button type="submit" class="btn btn-sm btn-primary" data-action="connect">
|
|
912
|
+
{% uj_icon "link", "fa-sm" %}
|
|
913
|
+
<span class="button-text">Connect</span>
|
|
914
|
+
</button>
|
|
915
|
+
<!-- Disconnect button (hidden by default) -->
|
|
916
|
+
<button type="submit" class="btn btn-sm btn-outline-danger d-none" data-action="disconnect">
|
|
917
|
+
{% uj_icon "unlink", "fa-sm" %}
|
|
918
|
+
<span class="button-text">Disconnect</span>
|
|
919
|
+
</button>
|
|
920
|
+
</form>
|
|
921
|
+
<small class="text-muted d-block mt-1" id="{{ connection.id }}-connection-status"></small>
|
|
922
|
+
</div>
|
|
896
923
|
</div>
|
|
897
924
|
</div>
|
|
898
925
|
</div>
|