Files
TenerifeProp/public/js/admin-components.js
TenerifeProp Dev bf6c477772 refactor: modular admin panel architecture
## Structure Created
- public/admin.html - main admin page (3251 lines)
- public/admin/*.html - component files:
  - sidebar.html (96 lines)
  - topbar.html (42 lines)
  - dashboard.html (198 lines)
  - properties.html (194 lines)
  - leads.html (185 lines)
  - testimonials.html (85 lines)
  - faq.html (95 lines)
  - services.html (89 lines)
  - settings.html (160 lines)
- public/css/admin.css (1135 lines)
- public/js/admin-components.js (247 lines)

## Clean URLs
- /login (was /login.html)
- /admin (was /admin.html)

## Issues Created
Milestone #52: Admin Panel Modular Refactoring
- #32: Dashboard - Statistics and Charts
- #33: Properties - CRUD Management
- #34: Leads - CRM Management
- #35: Testimonials - Management
- #36: FAQ - Management
- #37: Services - Management
- #38: Users - Management
- #39: Settings - Site Configuration

## TODO
Server routing needs update to serve:
- GET /admin/* -> public/admin/*.html
- GET /css/* -> public/css/*
- GET /js/* -> public/js/*

Current routes only handle SPA paths.
Components are ready but need server config.

## Verified
 Component files created
 CSS extracted (1135 lines)
 JS loader created (247 lines)
 All 8 admin sections modularized
 Clean URLs working (/login, /admin)
2026-04-06 02:00:22 +01:00

248 lines
7.9 KiB
JavaScript

// Admin Components Loader
class AdminApp {
constructor() {
this.currentUser = null;
this.currentSection = 'dashboard';
this.components = {
sidebar: '/admin/sidebar.html',
topbar: '/admin/topbar.html',
sections: {
dashboard: '/admin/dashboard.html',
properties: '/admin/properties.html',
leads: '/admin/leads.html',
testimonials: '/admin/testimonials.html',
faq: '/admin/faq.html',
services: '/admin/services.html',
settings: '/admin/settings.html'
}
};
}
async init() {
try {
await this.checkAuth();
await this.loadComponent('sidebar', '#sidebar-container');
await this.loadComponent('topbar', '#topbar-container');
await this.loadSection(this.currentSection);
this.setupNavigation();
this.setupTopbar();
console.log('Admin app initialized');
} catch (e) {
console.error('Init failed:', e);
window.location.href = '/login';
}
}
async checkAuth() {
const res = await API.getMe();
if (!res.success || !res.data) {
throw new Error('Not authenticated');
}
this.currentUser = res.data;
localStorage.setItem('user', JSON.stringify(this.currentUser));
}
async loadComponent(name, container) {
try {
const response = await fetch(this.components[name]);
if (!response.ok) throw new Error(`Failed to load ${name}`);
const html = await response.text();
const element = document.querySelector(container);
if (element) {
element.innerHTML = html;
this.executeScripts(element);
}
} catch (e) {
console.error(`Failed to load component ${name}:`, e);
}
}
async loadSection(sectionName) {
const sectionPath = this.components.sections[sectionName];
if (!sectionPath) {
console.error('Unknown section:', sectionName);
return;
}
try {
const response = await fetch(sectionPath);
if (!response.ok) throw new Error(`Failed to load section ${sectionName}`);
const html = await response.text();
// Hide all sections
document.querySelectorAll('.section').forEach(s => s.classList.remove('active'));
// Remove old section if exists
const oldSection = document.getElementById(`section-${sectionName}`);
if (oldSection) {
oldSection.remove();
}
// Add new section
const mainContent = document.getElementById('main-content');
if (mainContent) {
const div = document.createElement('div');
div.innerHTML = html;
const section = div.firstElementChild;
section.classList.add('active');
mainContent.appendChild(section);
this.executeScripts(section);
}
// Load section data
await this.loadSectionData(sectionName);
// Update page title
this.updatePageTitle(sectionName);
} catch (e) {
console.error(`Failed to load section ${sectionName}:`, e);
}
}
async loadSectionData(sectionName) {
const loaders = {
'dashboard': loadDashboard,
'properties': loadProperties,
'leads': loadLeads,
'testimonials': loadTestimonials,
'faq': loadFAQ,
'services': loadServices,
'settings': loadSettings
};
if (loaders[sectionName]) {
await loaders[sectionName]();
}
}
executeScripts(container) {
container.querySelectorAll('script').forEach(script => {
const newScript = document.createElement('script');
if (script.src) {
newScript.src = script.src;
} else {
newScript.textContent = script.textContent;
}
document.body.appendChild(newScript);
script.remove();
});
}
setupNavigation() {
document.querySelectorAll('.sidebar-link[data-section]').forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const section = link.dataset.section;
this.navigateTo(section);
});
});
}
setupTopbar() {
// Sidebar toggle
const toggle = document.getElementById('sidebarToggle');
if (toggle) {
toggle.addEventListener('click', () => {
document.getElementById('sidebar')?.classList.toggle('collapsed');
});
}
// Language switcher
document.querySelectorAll('.lang-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.lang-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const lang = btn.dataset.lang;
this.changeLanguage(lang);
});
});
// Update user info
this.updateUserInfo();
}
navigateTo(section) {
// Update nav
document.querySelectorAll('.sidebar-link').forEach(link => {
link.classList.remove('active');
});
document.querySelector(`.sidebar-link[data-section="${section}"]`)?.classList.add('active');
// Update hash
window.location.hash = section;
// Load section
this.currentSection = section;
this.loadSection(section);
}
updatePageTitle(section) {
const titles = {
'dashboard': 'Dashboard',
'properties': 'Propiedades',
'leads': 'Leads',
'testimonials': 'Testimonios',
'faq': 'FAQ',
'services': 'Servicios',
'settings': 'Configuración'
};
const titleEl = document.getElementById('pageTitle');
if (titleEl) {
titleEl.textContent = titles[section] || section;
}
}
updateUserInfo() {
if (!this.currentUser) return;
const userName = this.currentUser.name || 'Admin';
const userAvatar = userName.charAt(0).toUpperCase();
// Sidebar
document.getElementById('sidebarUserName').textContent = userName;
document.getElementById('sidebarUserRole').textContent = this.getRoleName(this.currentUser.role);
document.getElementById('sidebarAvatar').textContent = userAvatar;
// Topbar
document.getElementById('topbarUserName').textContent = userName;
document.getElementById('topbarUserRole').textContent = this.getRoleName(this.currentUser.role);
document.getElementById('topbarAvatar').src = `https://ui-avatars.com/api/?name=${encodeURIComponent(userName)}&background=1a5f4a&color=fff`;
}
getRoleName(role) {
const roles = {
admin: 'Administrador',
agent: 'Agente',
editor: 'Editor'
};
return roles[role] || role;
}
changeLanguage(lang) {
// TODO: Implement i18n
console.log('Language changed to:', lang);
}
}
// Initialize app
const app = new AdminApp();
document.addEventListener('DOMContentLoaded', () => app.init());
// Handle hash navigation
window.addEventListener('hashchange', () => {
const section = window.location.hash.substring(1);
if (section) app.navigateTo(section);
});
// Logout function
function logout() {
API.logout().then(() => {
localStorage.removeItem('user');
window.location.href = '/login';
}).catch(() => {
window.location.href = '/login';
});
}