diff --git a/js/components.js b/js/components.js index 976b61e..3c9be96 100644 --- a/js/components.js +++ b/js/components.js @@ -1,514 +1,515 @@ -/** - * UI Components module for AegisSight website - * Contains all interactive UI component logic - */ - -// Language Toggle Component -const LanguageToggle = { - element: null, - - /** - * Initialize language toggle - */ - init() { - this.element = document.querySelector(SELECTORS.LANG_TOGGLE); - if (!this.element) return; - - this.element.addEventListener('click', (e) => { - e.preventDefault(); - e.stopPropagation(); - this.toggle(); - }); - }, - - /** - * Toggle between languages - */ - toggle() { - const newLanguage = getCurrentLanguage() === 'de' ? 'en' : 'de'; - switchLanguage(newLanguage); - - // Update expand button text after language change - ProductShowcase.updateExpandButtonText(); - } -}; - -// Navigation Component -const Navigation = { - navbar: null, - - /** - * Initialize navigation component - */ - init() { - this.navbar = document.querySelector(SELECTORS.NAVBAR); - this.setupSmoothScrolling(); - this.setupMobileMenu(); - }, - - /** - * Setup smooth scrolling for anchor links - */ - setupSmoothScrolling() { - document.querySelectorAll(SELECTORS.SMOOTH_LINKS).forEach(anchor => { - anchor.addEventListener('click', function(e) { - e.preventDefault(); - const targetId = this.getAttribute('href'); - const target = document.querySelector(targetId); - - if (target) { - target.scrollIntoView({ - behavior: 'smooth', - block: 'start' - }); - } - }); - }); - }, - - /** - * Setup mobile menu functionality - */ - setupMobileMenu() { - // Mobile menu logic would go here if needed - // Currently not implemented as per YAGNI principle - } -}; - -// About Section Tabs -const AboutTabs = { - tabs: null, - panels: null, - - /** - * Initialize about section tabs - */ - init() { - this.tabs = document.querySelectorAll(SELECTORS.ABOUT_TABS); - this.panels = document.querySelectorAll(SELECTORS.ABOUT_PANELS); - - if (!this.tabs.length) return; - - this.tabs.forEach(tab => { - tab.addEventListener('click', () => this.switchTab(tab)); - }); - }, - - /** - * Switch to selected tab - * @param {HTMLElement} selectedTab - Tab element that was clicked - */ - switchTab(selectedTab) { - const targetPanelId = selectedTab.getAttribute(DATA_ATTRS.TAB); - - // Remove active class from all tabs and panels - this.tabs.forEach(tab => tab.classList.remove(CLASSES.ACTIVE)); - this.panels.forEach(panel => panel.classList.remove(CLASSES.ACTIVE)); - - // Add active class to selected tab and corresponding panel - selectedTab.classList.add(CLASSES.ACTIVE); - const targetPanel = document.getElementById(targetPanelId); - if (targetPanel) { - targetPanel.classList.add(CLASSES.ACTIVE); - } - } -}; - -// Product Showcase Component -const ProductShowcase = { - expandButton: null, - toolsGrid: null, - - /** - * Initialize product showcase - */ - init() { - this.expandButton = document.querySelector(SELECTORS.EXPAND_BUTTON); - this.toolsGrid = document.querySelector(SELECTORS.TOOLS_GRID); - - if (!this.expandButton || !this.toolsGrid) return; - - this.expandButton.addEventListener('click', () => this.toggleExpand()); - }, - - /** - * Toggle expand/collapse state - */ - toggleExpand() { - const isExpanded = this.expandButton.getAttribute(DATA_ATTRS.EXPANDED) === 'true'; - - if (isExpanded) { - this.collapse(); - } else { - this.expand(); - } - }, - - /** - * Expand the tools grid - */ - expand() { - this.toolsGrid.classList.remove(CLASSES.COLLAPSED); - this.expandButton.setAttribute(DATA_ATTRS.EXPANDED, 'true'); - this.updateExpandButtonText(); - }, - - /** - * Collapse the tools grid - */ - collapse() { - this.toolsGrid.classList.add(CLASSES.COLLAPSED); - this.expandButton.setAttribute(DATA_ATTRS.EXPANDED, 'false'); - this.updateExpandButtonText(); - }, - - /** - * Update expand button text based on state - */ - updateExpandButtonText() { - const expandText = this.expandButton?.querySelector('.expand-text'); - if (!expandText) return; - - const isExpanded = this.expandButton.getAttribute(DATA_ATTRS.EXPANDED) === 'true'; - expandText.textContent = getTranslation(isExpanded ? 'hideDetails' : 'expandDetails'); - } -}; - -// Login Modal Component -const LoginModal = { - modalElement: null, - modalStyles: null, - - /** - * Show login modal - */ - show() { - this.createModal(); - this.attachEventListeners(); - }, - - /** - * Create modal HTML and styles - */ - createModal() { - // Create modal element - this.modalElement = document.createElement('div'); - this.modalElement.className = 'login-modal'; - this.modalElement.innerHTML = this.getModalHTML(); - document.body.appendChild(this.modalElement); - - // Add modal styles if not already added - if (!this.modalStyles) { - this.addModalStyles(); - } - }, - - /** - * Get modal HTML content - * @returns {string} Modal HTML - */ - getModalHTML() { - const t = getTranslation; - return ` - - `; - }, - - /** - * Add modal styles to document - */ - addModalStyles() { - this.modalStyles = document.createElement('style'); - this.modalStyles.textContent = ` - .login-modal { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.7); - backdrop-filter: blur(10px); - display: flex; - align-items: center; - justify-content: center; - z-index: 10000; - animation: fadeIn 0.3s ease; - } - .modal-content { - background: #ffffff; - border-radius: 12px; - padding: 2.5rem; - max-width: 400px; - width: 90%; - position: relative; - box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); - } - .modal-header { - text-align: center; - margin-bottom: 1.5rem; - } - .modal-header .lock-icon { - width: 48px; - height: 48px; - color: #0066cc; - margin-bottom: 1rem; - } - .modal-close { - position: absolute; - top: 1rem; - right: 1rem; - background: none; - border: none; - color: #666; - font-size: 2rem; - cursor: pointer; - transition: all 0.3s ease; - width: 32px; - height: 32px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 50%; - } - .modal-close:hover { - background: #f0f0f0; - color: #333; - } - .modal-content h3 { - color: #1a1a1a; - margin-bottom: 0.5rem; - font-size: 1.5rem; - font-weight: 600; - } - .modal-content p { - color: #666; - margin-bottom: 2rem; - text-align: center; - } - .modal-content .form-group { - margin-bottom: 1.5rem; - } - .modal-content label { - display: block; - color: #333; - margin-bottom: 0.5rem; - font-weight: 500; - } - .modal-content input { - width: 100%; - padding: 0.875rem; - background: #f8f8f8; - border: 2px solid #e0e0e0; - border-radius: 8px; - color: #333; - font-size: 1rem; - transition: all 0.3s ease; - } - .modal-content input:focus { - outline: none; - border-color: #0066cc; - background: #fff; - } - .modal-content input::placeholder { - color: #999; - } - .modal-content .primary-button { - width: 100%; - padding: 0.875rem; - background: #0066cc; - color: white; - border: none; - border-radius: 8px; - font-size: 1rem; - font-weight: 600; - cursor: pointer; - transition: all 0.3s ease; - } - .modal-content .primary-button:hover { - background: #0052a3; - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(0, 102, 204, 0.3); - } - .auth-note { - text-align: center; - margin-top: 1.5rem; - font-size: 0.9rem; - color: #666; - } - .auth-note a { - color: #0066cc; - text-decoration: none; - } - .auth-note a:hover { - text-decoration: underline; - } - `; - document.head.appendChild(this.modalStyles); - }, - - /** - * Attach event listeners to modal - */ - attachEventListeners() { - // Close button - const closeBtn = this.modalElement.querySelector('.modal-close'); - closeBtn.addEventListener('click', () => this.close()); - - // Form submission - const form = this.modalElement.querySelector('#loginForm'); - form.addEventListener('submit', (e) => this.handleSubmit(e)); - - // Click outside to close - this.modalElement.addEventListener('click', (e) => { - if (e.target === this.modalElement) { - this.close(); - } - }); - }, - - /** - * Handle form submission - * @param {Event} e - Submit event - */ - async handleSubmit(e) { - e.preventDefault(); - const password = document.getElementById('auth-password').value; - - try { - // Validate token via Insights API - const response = await fetch('/insights/api/validate-token', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ token: password }) - }); - - const result = await response.json(); - - if (result.valid) { - sessionStorage.setItem(CONFIG.AUTH.SESSION_KEY, 'true'); - this.close(); - window.location.href = CONFIG.AUTH.REDIRECT_PAGE; - } else { - alert(getTranslation('wrongCode')); - document.getElementById('auth-password').value = ''; - document.getElementById('auth-password').focus(); - } - } catch (error) { - console.error('Token validation error:', error); - alert(getTranslation('wrongCode')); - } - }, - - /** - * Close and remove modal - */ - close() { - if (this.modalElement) { - this.modalElement.remove(); - this.modalElement = null; - } - } -}; - -// Contact Form Component -const ContactForm = { - form: null, - - /** - * Initialize contact form - */ - init() { - this.form = document.querySelector(SELECTORS.CONTACT_FORM); - if (!this.form) return; - - this.form.addEventListener('submit', (e) => this.handleSubmit(e)); - }, - - /** - * Handle form submission - * @param {Event} e - Submit event - */ - handleSubmit(e) { - e.preventDefault(); - - // Get form data - const formData = new FormData(this.form); - const data = Object.fromEntries(formData.entries()); - - // In production, this would send data to server - console.log('Form submission:', data); - - // Show success message - alert(getTranslation('contactFormSuccess')); - this.form.reset(); - } -}; - -// Demo Request Handler -const DemoRequest = { - /** - * Initialize demo request buttons - */ - init() { - document.querySelectorAll('.primary-button, .secondary-button, .cta-button').forEach(button => { - if (button.textContent.toLowerCase().includes('demo')) { - button.addEventListener('click', (e) => this.handleDemoRequest(e)); - } - }); - }, - - /** - * Handle demo request - * @param {Event} e - Click event - */ - handleDemoRequest(e) { - e.preventDefault(); - alert(getTranslation('demoRequestAlert')); - } -}; - -// Initialize all components -const Components = { - /** - * Initialize all UI components - */ - init() { - LanguageToggle.init(); - Navigation.init(); - AboutTabs.init(); - ProductShowcase.init(); - ContactForm.init(); - DemoRequest.init(); - } -}; - -// Make showLoginModal globally available for onclick attribute -window.showLoginModal = function() { - LoginModal.show(); -}; - -// Make closeLoginModal globally available for onclick attribute -window.closeLoginModal = function() { - LoginModal.close(); +/** + * UI Components module for AegisSight website + * Contains all interactive UI component logic + */ + +// Language Toggle Component +const LanguageToggle = { + element: null, + + /** + * Initialize language toggle + */ + init() { + this.element = document.querySelector(SELECTORS.LANG_TOGGLE); + if (!this.element) return; + + this.element.addEventListener('click', (e) => { + e.preventDefault(); + e.stopPropagation(); + this.toggle(); + }); + }, + + /** + * Toggle between languages + */ + toggle() { + const newLanguage = getCurrentLanguage() === 'de' ? 'en' : 'de'; + switchLanguage(newLanguage); + + // Update expand button text after language change + ProductShowcase.updateExpandButtonText(); + } +}; + +// Navigation Component +const Navigation = { + navbar: null, + + /** + * Initialize navigation component + */ + init() { + this.navbar = document.querySelector(SELECTORS.NAVBAR); + this.setupSmoothScrolling(); + this.setupMobileMenu(); + }, + + /** + * Setup smooth scrolling for anchor links + */ + setupSmoothScrolling() { + document.querySelectorAll(SELECTORS.SMOOTH_LINKS).forEach(anchor => { + anchor.addEventListener('click', function(e) { + e.preventDefault(); + const targetId = this.getAttribute('href'); + const target = document.querySelector(targetId); + + if (target) { + target.scrollIntoView({ + behavior: 'smooth', + block: 'start' + }); + } + }); + }); + }, + + /** + * Setup mobile menu functionality + */ + setupMobileMenu() { + // Mobile menu logic would go here if needed + // Currently not implemented as per YAGNI principle + } +}; + +// About Section Tabs +const AboutTabs = { + tabs: null, + panels: null, + + /** + * Initialize about section tabs + */ + init() { + this.tabs = document.querySelectorAll(SELECTORS.ABOUT_TABS); + this.panels = document.querySelectorAll(SELECTORS.ABOUT_PANELS); + + if (!this.tabs.length) return; + + this.tabs.forEach(tab => { + tab.addEventListener('click', () => this.switchTab(tab)); + }); + }, + + /** + * Switch to selected tab + * @param {HTMLElement} selectedTab - Tab element that was clicked + */ + switchTab(selectedTab) { + const targetPanelId = selectedTab.getAttribute(DATA_ATTRS.TAB); + + // Remove active class from all tabs and panels + this.tabs.forEach(tab => tab.classList.remove(CLASSES.ACTIVE)); + this.panels.forEach(panel => panel.classList.remove(CLASSES.ACTIVE)); + + // Add active class to selected tab and corresponding panel + selectedTab.classList.add(CLASSES.ACTIVE); + const targetPanel = document.getElementById(targetPanelId); + if (targetPanel) { + targetPanel.classList.add(CLASSES.ACTIVE); + } + } +}; + +// Product Showcase Component +const ProductShowcase = { + expandButton: null, + toolsGrid: null, + + /** + * Initialize product showcase + */ + init() { + this.expandButton = document.querySelector(SELECTORS.EXPAND_BUTTON); + this.toolsGrid = document.querySelector(SELECTORS.TOOLS_GRID); + + if (!this.expandButton || !this.toolsGrid) return; + + this.expandButton.addEventListener('click', () => this.toggleExpand()); + }, + + /** + * Toggle expand/collapse state + */ + toggleExpand() { + const isExpanded = this.expandButton.getAttribute(DATA_ATTRS.EXPANDED) === 'true'; + + if (isExpanded) { + this.collapse(); + } else { + this.expand(); + } + }, + + /** + * Expand the tools grid + */ + expand() { + this.toolsGrid.classList.remove(CLASSES.COLLAPSED); + this.expandButton.setAttribute(DATA_ATTRS.EXPANDED, 'true'); + this.updateExpandButtonText(); + }, + + /** + * Collapse the tools grid + */ + collapse() { + this.toolsGrid.classList.add(CLASSES.COLLAPSED); + this.expandButton.setAttribute(DATA_ATTRS.EXPANDED, 'false'); + this.updateExpandButtonText(); + }, + + /** + * Update expand button text based on state + */ + updateExpandButtonText() { + const expandText = this.expandButton?.querySelector('.expand-text'); + if (!expandText) return; + + const isExpanded = this.expandButton.getAttribute(DATA_ATTRS.EXPANDED) === 'true'; + expandText.textContent = getTranslation(isExpanded ? 'hideDetails' : 'expandDetails'); + } +}; + +// Login Modal Component +const LoginModal = { + modalElement: null, + modalStyles: null, + + /** + * Show login modal + */ + show() { + this.createModal(); + this.attachEventListeners(); + }, + + /** + * Create modal HTML and styles + */ + createModal() { + // Create modal element + this.modalElement = document.createElement('div'); + this.modalElement.className = 'login-modal'; + this.modalElement.innerHTML = this.getModalHTML(); + document.body.appendChild(this.modalElement); + + // Add modal styles if not already added + if (!this.modalStyles) { + this.addModalStyles(); + } + }, + + /** + * Get modal HTML content + * @returns {string} Modal HTML + */ + getModalHTML() { + const t = getTranslation; + return ` + + `; + }, + + /** + * Add modal styles to document + */ + addModalStyles() { + this.modalStyles = document.createElement('style'); + this.modalStyles.textContent = ` + .login-modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(10, 24, 50, 0.85); + backdrop-filter: blur(12px); + display: flex; + align-items: center; + justify-content: center; + z-index: 10000; + animation: fadeIn 0.3s ease; + } + .modal-content { + background: #0A1832; + border-radius: 12px; + padding: 2.5rem; + max-width: 400px; + width: 90%; + position: relative; + border: 1px solid rgba(200, 168, 81, 0.3); + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); + } + .modal-header { + text-align: center; + margin-bottom: 1.5rem; + } + .modal-header .lock-icon { + width: 48px; + height: 48px; + color: #C8A851; + margin-bottom: 1rem; + } + .modal-close { + position: absolute; + top: 1rem; + right: 1rem; + background: none; + border: none; + color: rgba(255, 255, 255, 0.4); + font-size: 2rem; + cursor: pointer; + transition: all 0.3s ease; + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + } + .modal-close:hover { + background: rgba(255, 255, 255, 0.1); + color: #fff; + } + .modal-content h3 { + color: #FFFFFF; + margin-bottom: 0.5rem; + font-size: 1.5rem; + font-weight: 600; + } + .modal-content p { + color: rgba(255, 255, 255, 0.6); + margin-bottom: 2rem; + text-align: center; + } + .modal-content .form-group { + margin-bottom: 1.5rem; + } + .modal-content label { + display: block; + color: rgba(255, 255, 255, 0.8); + margin-bottom: 0.5rem; + font-weight: 500; + } + .modal-content input { + width: 100%; + padding: 0.875rem; + background: rgba(255, 255, 255, 0.05); + border: 2px solid rgba(255, 255, 255, 0.15); + border-radius: 8px; + color: #FFFFFF; + font-size: 1rem; + transition: all 0.3s ease; + } + .modal-content input:focus { + outline: none; + border-color: #C8A851; + background: rgba(255, 255, 255, 0.08); + } + .modal-content input::placeholder { + color: rgba(255, 255, 255, 0.3); + } + .modal-content .primary-button { + width: 100%; + padding: 0.875rem; + background: #C8A851; + color: #0A1832; + border: none; + border-radius: 8px; + font-size: 1rem; + font-weight: 600; + cursor: pointer; + transition: all 0.3s ease; + } + .modal-content .primary-button:hover { + background: #D4B96A; + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(200, 168, 81, 0.4); + } + .auth-note { + text-align: center; + margin-top: 1.5rem; + font-size: 0.9rem; + color: rgba(255, 255, 255, 0.4); + } + .auth-note a { + color: #C8A851; + text-decoration: none; + } + .auth-note a:hover { + text-decoration: underline; + } + `; + document.head.appendChild(this.modalStyles); + }, + + /** + * Attach event listeners to modal + */ + attachEventListeners() { + // Close button + const closeBtn = this.modalElement.querySelector('.modal-close'); + closeBtn.addEventListener('click', () => this.close()); + + // Form submission + const form = this.modalElement.querySelector('#loginForm'); + form.addEventListener('submit', (e) => this.handleSubmit(e)); + + // Click outside to close + this.modalElement.addEventListener('click', (e) => { + if (e.target === this.modalElement) { + this.close(); + } + }); + }, + + /** + * Handle form submission + * @param {Event} e - Submit event + */ + async handleSubmit(e) { + e.preventDefault(); + const password = document.getElementById('auth-password').value; + + try { + // Validate token via Insights API + const response = await fetch('/insights/api/validate-token', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ token: password }) + }); + + const result = await response.json(); + + if (result.valid) { + sessionStorage.setItem(CONFIG.AUTH.SESSION_KEY, 'true'); + this.close(); + window.location.href = CONFIG.AUTH.REDIRECT_PAGE; + } else { + alert(getTranslation('wrongCode')); + document.getElementById('auth-password').value = ''; + document.getElementById('auth-password').focus(); + } + } catch (error) { + console.error('Token validation error:', error); + alert(getTranslation('wrongCode')); + } + }, + + /** + * Close and remove modal + */ + close() { + if (this.modalElement) { + this.modalElement.remove(); + this.modalElement = null; + } + } +}; + +// Contact Form Component +const ContactForm = { + form: null, + + /** + * Initialize contact form + */ + init() { + this.form = document.querySelector(SELECTORS.CONTACT_FORM); + if (!this.form) return; + + this.form.addEventListener('submit', (e) => this.handleSubmit(e)); + }, + + /** + * Handle form submission + * @param {Event} e - Submit event + */ + handleSubmit(e) { + e.preventDefault(); + + // Get form data + const formData = new FormData(this.form); + const data = Object.fromEntries(formData.entries()); + + // In production, this would send data to server + console.log('Form submission:', data); + + // Show success message + alert(getTranslation('contactFormSuccess')); + this.form.reset(); + } +}; + +// Demo Request Handler +const DemoRequest = { + /** + * Initialize demo request buttons + */ + init() { + document.querySelectorAll('.primary-button, .secondary-button, .cta-button').forEach(button => { + if (button.textContent.toLowerCase().includes('demo')) { + button.addEventListener('click', (e) => this.handleDemoRequest(e)); + } + }); + }, + + /** + * Handle demo request + * @param {Event} e - Click event + */ + handleDemoRequest(e) { + e.preventDefault(); + alert(getTranslation('demoRequestAlert')); + } +}; + +// Initialize all components +const Components = { + /** + * Initialize all UI components + */ + init() { + LanguageToggle.init(); + Navigation.init(); + AboutTabs.init(); + ProductShowcase.init(); + ContactForm.init(); + DemoRequest.init(); + } +}; + +// Make showLoginModal globally available for onclick attribute +window.showLoginModal = function() { + LoginModal.show(); +}; + +// Make closeLoginModal globally available for onclick attribute +window.closeLoginModal = function() { + LoginModal.close(); }; \ No newline at end of file