ultimate-jekyll-manager
Version:
Ultimate Jekyll dependency manager
346 lines (289 loc) • 9.73 kB
JavaScript
// Libraries
import fetch from 'wonderful-fetch';
import * as profileSection from './sections/profile.js';
import * as notificationsSection from './sections/notifications.js';
import * as securitySection from './sections/security.js';
import * as billingSection from './sections/billing.js';
import * as teamSection from './sections/team.js';
import * as referralsSection from './sections/referrals.js';
import * as apiKeysSection from './sections/api-keys.js';
import * as deleteSection from './sections/delete.js';
import * as connectionsSection from './sections/connections.js';
let webManager = null;
// Module
export default (Manager) => {
return new Promise(async function (resolve) {
// Shortcuts
webManager = Manager.webManager;
// Initialize when DOM is ready
await webManager.dom().ready();
try {
await initializeAccount();
} catch (error) {
webManager.sentry().captureException(new Error('Failed to initialize account page', { cause: error }));
}
// Resolve after initialization
return resolve();
});
};
// Global state
let $navLinks = null;
let $sections = null;
let appData = null;
let fetchAppDataPromise = null;
// Section modules map
const sectionModules = {
profile: profileSection,
notifications: notificationsSection,
security: securitySection,
billing: billingSection,
team: teamSection,
referrals: referralsSection,
'api-keys': apiKeysSection,
delete: deleteSection,
connections: connectionsSection
};
// Main initialization
async function initializeAccount() {
// Get DOM elements
$navLinks = document.querySelectorAll('#account-nav .nav-link');
$sections = document.querySelectorAll('.account-section');
// Initially hide all sections except loading
hideAllSectionsExceptLoading();
// Setup navigation
setupNavigation();
// Check if delete hash is present on initial load
if (window.location.hash === '#delete') {
showDeleteOption();
}
// Initialize all section modules
Object.values(sectionModules).forEach(module => {
if (module.init) {
module.init(webManager);
}
});
// Setup auth listener with account data
webManager.auth().listen({ account: true }, async (state) => {
console.log('Auth state with account data:', state);
/* @dev-only:start */
{
// Check for test subscription parameter
const urlParams = new URLSearchParams(window.location.search);
const testSubscription = urlParams.get('_dev_subscription');
if (testSubscription && webManager.isDevelopment()) {
try {
console.log(`Loading test subscription: ${testSubscription}`);
const testModule = await import(`./test-subscriptions/${testSubscription}.js`);
// Override the account subscription with test data
if (state.account) {
state.account.subscription = testModule.default;
console.log('Test subscription loaded:', testModule.default);
}
} catch (error) {
console.error(`Failed to load test subscription '${testSubscription}':`, error);
}
}
}
/* @dev-only:end */
// Load user data with the account information
// Wait for app data to be fetched before loading section data
await fetchAppData();
loadAllSectionData(state);
// Hide loading section and show the appropriate section
hideLoadingSection();
// Handle initial hash or show default section
handleHashChange();
});
}
// Fetch app data to get configuration and OAuth settings
async function fetchAppData() {
if (fetchAppDataPromise) return fetchAppDataPromise;
fetchAppDataPromise = (async () => {
try {
// Get app ID from site configuration
const appId = webManager.config.brand.id;
const apiBaseUrl = 'https://api.itwcreativeworks.com';
// Fetch app data
const response = await fetch(`${apiBaseUrl}/get-app`, {
response: 'json',
query: {
id: appId,
}
});
console.log('Fetched app data:', response);
appData = response;
return response;
} catch (error) {
webManager.sentry().captureException(new Error('Failed to fetch app data', { cause: error }));
return null;
}
})();
return fetchAppDataPromise;
}
// Load data for all sections
function loadAllSectionData(authState) {
const { user, account } = authState;
// Load data for each section (passing app data where needed)
if (sectionModules.profile.loadData) {
sectionModules.profile.loadData(account, user);
}
if (sectionModules.notifications.loadData) {
sectionModules.notifications.loadData(account);
}
if (sectionModules.security.loadData) {
sectionModules.security.loadData(account);
}
if (sectionModules.billing.loadData) {
sectionModules.billing.loadData(account, appData);
}
if (sectionModules.team && sectionModules.team.loadData) {
sectionModules.team.loadData(account);
}
if (sectionModules.referrals && sectionModules.referrals.loadData) {
sectionModules.referrals.loadData(account);
}
if (sectionModules['api-keys'] && sectionModules['api-keys'].loadData) {
sectionModules['api-keys'].loadData(account);
}
if (sectionModules.delete.loadData) {
sectionModules.delete.loadData(account);
}
if (sectionModules.connections.loadData) {
sectionModules.connections.loadData(account, appData);
}
}
// Setup navigation between sections
function setupNavigation() {
// Add click listeners to nav links
$navLinks.forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const sectionId = link.dataset.section;
showSection(sectionId);
// Update URL hash
window.location.hash = sectionId;
});
});
// Setup mobile dropdown navigation
const $mobileNavSelect = document.getElementById('mobile-nav-select');
if ($mobileNavSelect) {
$mobileNavSelect.addEventListener('change', (e) => {
const sectionId = e.target.value;
showSection(sectionId);
window.location.hash = sectionId;
});
}
// Listen for hash changes
window.addEventListener('hashchange', handleHashChange);
}
// Handle hash changes
function handleHashChange() {
const hash = window.location.hash.slice(1);
// Show delete nav item and mobile option if hash is delete
if (hash === 'delete') {
showDeleteOption();
}
if (hash) {
// Check if the section exists
const $targetSection = document.getElementById(`${hash}-section`);
if ($targetSection) {
showSection(hash);
} else {
// Section doesn't exist, default to profile
console.warn(`Section "${hash}" not found, defaulting to profile`);
webManager.sentry().captureException(new Error(`Invalid account section hash: ${hash}`));
window.location.hash = '#profile';
showSection('profile');
}
} else {
// No hash, show profile by default
showSection('profile');
}
}
// Show delete option in navigation
function showDeleteOption() {
// Show desktop nav item
const $deleteNavItem = document.getElementById('delete-nav-item');
if ($deleteNavItem) {
$deleteNavItem.classList.remove('d-none');
}
// Add mobile dropdown option if not exists
const $mobileNavSelect = document.getElementById('mobile-nav-select');
if ($mobileNavSelect) {
const deleteOption = $mobileNavSelect.querySelector('option[value="delete"]');
if (!deleteOption) {
const option = document.createElement('option');
option.value = 'delete';
option.textContent = 'Delete Account';
option.className = 'text-danger';
$mobileNavSelect.appendChild(option);
}
}
}
// Hide all sections except loading
function hideAllSectionsExceptLoading() {
$sections.forEach(section => {
if (section.id !== 'loading-section') {
section.classList.add('d-none');
}
});
}
// Hide loading section
function hideLoadingSection() {
const $loadingSection = document.getElementById('loading-section');
if ($loadingSection) {
$loadingSection.classList.add('d-none');
}
}
// Show specific section
function showSection(sectionId) {
// Don't show sections while loading
const $loadingSection = document.getElementById('loading-section');
if ($loadingSection && !$loadingSection.classList.contains('d-none')) {
// Still loading, don't switch sections yet
return;
}
// Hide all sections
$sections.forEach(section => {
section.classList.add('d-none');
});
// Show selected section
const $targetSection = document.getElementById(`${sectionId}-section`);
if ($targetSection) {
$targetSection.classList.remove('d-none');
// Track section view when actually shown
trackAccountSectionView(sectionId);
}
// Update nav active state
$navLinks.forEach(link => {
if (link.dataset.section === sectionId) {
link.classList.add('active');
} else {
link.classList.remove('active');
}
});
// Update mobile dropdown to match
const $mobileNavSelect = document.getElementById('mobile-nav-select');
if ($mobileNavSelect) {
$mobileNavSelect.value = sectionId;
}
// Call section's onShow method if it exists
const sectionModule = sectionModules[sectionId];
if (sectionModule && sectionModule.onShow) {
sectionModule.onShow();
}
}
// Tracking functions
function trackAccountSectionView(sectionId) {
gtag('event', 'account_section_view', {
section_name: sectionId
});
fbq('trackCustom', 'AccountSectionView', {
section: sectionId
});
ttq.track('ViewContent', {
content_id: `account-${sectionId}`,
content_type: 'product',
content_name: `Account ${sectionId}`
});
}