/* jshint esversion: 2024, module: true */ import { ApiClient } from "./api-client.js"; import { ConfigManager } from "./config-manager.js"; import { InterfaceRenderer } from "./interface-renderer.js"; import { ThemeManager } from "./theme-manager.js"; /** * Main Application Class * @class Application */ class Application { /** * @param {Object} elements - DOM elements */ constructor(elements) { this.elements = elements; this.state = { currentInterface: null, interfaces: [], editorMode: "raw", }; // Initialize modules this.themeManager = new ThemeManager(elements); this.apiClient = new ApiClient(); this.interfaceRenderer = new InterfaceRenderer(elements, this.state); this.configManager = new ConfigManager( elements, this.apiClient, this.state, ); } /** * Initialize the application * @method init */ init() { this.themeManager.init(); this.setupEventListeners(); this.loadStatus(); } /** * Set up all event listeners * @method setupEventListeners */ setupEventListeners() { // Navigation this.elements.buttons.nav.forEach((button) => { button.addEventListener("click", (event) => { this.show(event.currentTarget.dataset.panel); }); }); // Status panel this.elements.buttons.refreshStatus?.addEventListener("click", () => this.loadStatus(), ); // Configs panel - delegated to ConfigManager this.configManager.setupEventListeners(); // Logs panel this.elements.buttons.refreshLogs?.addEventListener("click", () => this.loadLogs(), ); // Commands panel this.elements.buttons.restartNetworkd?.addEventListener("click", () => this.restartNetworkd(), ); this.elements.buttons.rebootDevice?.addEventListener("click", () => this.rebootDevice(), ); // Touch support document.addEventListener("touchstart", this.handleTouchStart, { passive: true, }); } /** * Handle touch events for better mobile support * @method handleTouchStart * @param {TouchEvent} event */ handleTouchStart = (event) => { // Add visual feedback for touch if ( event.target.classList.contains("button") || event.target.classList.contains("nav-button") ) { event.target.style.opacity = "0.7"; setTimeout(() => { event.target.style.opacity = ""; }, 150); } }; /** * Show specified panel and hide others * @method show * @param {string} panel - Panel to show */ show(panel) { // Hide all panels and remove active class from buttons Object.values(this.elements.panels).forEach((p) => p?.classList.remove("active"), ); this.elements.buttons.nav.forEach((btn) => btn?.classList.remove("active")); // Show selected panel and activate button this.elements.panels[panel]?.classList.add("active"); document.querySelector(`[data-panel="${panel}"]`)?.classList.add("active"); // Load panel-specific data const panelActions = { status: () => this.loadStatus(), configs: () => this.configManager.refreshConfigs(), logs: () => this.loadLogs(), }; panelActions[panel]?.(); } /** * Load and display network status * @method loadStatus */ async loadStatus() { try { const data = await this.apiClient.get("/api/status"); this.state.interfaces = data.Interfaces ?? []; this.interfaceRenderer.renderInterfaceTabs(this.state.interfaces); // Show first interface by default if (this.state.interfaces.length > 0 && !this.state.currentInterface) { this.interfaceRenderer.showInterfaceDetails(this.state.interfaces[0]); } } catch (error) { this.elements.outputs.ifaceDetails.innerHTML = `
`; } } /** * Load system logs * @method loadLogs */ async loadLogs() { try { const text = await this.apiClient.getText("/api/logs"); this.elements.outputs.logsArea.textContent = text; } catch (error) { this.elements.outputs.logsArea.textContent = `Error: ${error.message}`; } } /** * Restart networkd service * @method restartNetworkd */ async restartNetworkd() { if (!confirm("Restart systemd-networkd? Active connections may be reset.")) return; try { const result = await this.apiClient.post("/api/reload"); this.elements.outputs.cmdResult.textContent = `Success: ${JSON.stringify(result)}`; } catch (error) { this.elements.outputs.cmdResult.textContent = `Error: ${error.message}`; } } /** * Reboot the device * @method rebootDevice */ async rebootDevice() { if (!confirm("Reboot device now?")) return; try { const result = await this.apiClient.post("/api/reboot"); this.elements.outputs.cmdResult.textContent = `Success: ${JSON.stringify(result)}`; } catch (error) { this.elements.outputs.cmdResult.textContent = `Error: ${error.message}`; } } } // Initialize application when DOM is loaded document.addEventListener("DOMContentLoaded", () => { const elements = { themeToggle: document.getElementById("themeToggle"), themeIcon: document.getElementById("themeIcon"), panels: { status: document.getElementById("panelStatus"), configs: document.getElementById("panelConfigs"), logs: document.getElementById("panelLogs"), commands: document.getElementById("panelCommands"), }, buttons: { nav: document.querySelectorAll(".nav-button"), refreshStatus: document.getElementById("refreshStatus"), refreshConfigs: document.getElementById("refreshConfigs"), saveConfig: document.getElementById("saveConfig"), validateConfig: document.getElementById("validateConfig"), refreshLogs: document.getElementById("refreshLogs"), restartNetworkd: document.getElementById("restartNetworkd"), rebootDevice: document.getElementById("rebootDevice"), }, inputs: { configSelect: document.getElementById("configSelect"), cfgEditor: document.getElementById("cfgEditor"), restartAfterSave: document.getElementById("restartAfterSave"), }, outputs: { ifaceTabs: document.getElementById("interfaceTabs"), ifaceDetails: document.getElementById("interfaceDetails"), validateResult: document.getElementById("validateResult"), logsArea: document.getElementById("logsArea"), cmdResult: document.getElementById("cmdResult"), }, }; const app = new Application(elements); app.init(); // Make app globally available for debugging window.app = app; }); export { Application };