- Remove duplicate </style></head><body> tags - Remove duplicate script tags and incomplete JavaScript - Add URL hash handling (e.g., /admin#dashboard, /admin#properties) - Add hashchange event listener for browser back/forward navigation - Simplify navigation by updating hash instead of directly calling loadSection Fixes issue where URL didn't change when navigating between admin sections.
1581 lines
48 KiB
HTML
1581 lines
48 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Admin Panel - TenerifeProp</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css" rel="stylesheet">
|
|
<link href="https://cdn.jsdelivr.net/npm/datatables.net-bs5@1.13.8/css/dataTables.bootstrap5.min.css" rel="stylesheet">
|
|
<link href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css" rel="stylesheet">
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
|
<!-- Chart.js -->
|
|
<link href="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.min.css" rel="stylesheet">
|
|
<!-- DataTables -->
|
|
<link href="https://cdn.jsdelivr.net/npm/datatables.net-bs5@1.13.8/css/dataTables.bootstrap5.min.css" rel="stylesheet">
|
|
<!-- Lightpick -->
|
|
<link href="https://cdn.jsdelivr.net/npm/lightpick@1.6.2/css/lightpick.min.css" rel="stylesheet">
|
|
|
|
<style>
|
|
:root {
|
|
--primary: #1a5f4a;
|
|
--primary-light: #2d8f6f;
|
|
--primary-dark: #0d4535;
|
|
--secondary: #d4a853;
|
|
--accent: #e85d04;
|
|
--success: #10b981;
|
|
--warning: #f59e0b;
|
|
--danger: #ef4444;
|
|
--info: #3b82f6;
|
|
|
|
/* Dark Theme */
|
|
--sidebar-bg: #0f172a;
|
|
--sidebar-hover: #1e293b;
|
|
--sidebar-active: #1a5f4a;
|
|
--sidebar-text: #94a3b8;
|
|
--sidebar-text-active: #ffffff;
|
|
|
|
--body-bg: #f1f5f9;
|
|
--card-bg: #ffffff;
|
|
--card-border: #e2e8f0;
|
|
--text-primary: #1e293b;
|
|
--text-secondary: #64748b;
|
|
--text-muted: #94a3b8;
|
|
|
|
--sidebar-width: 280px;
|
|
--topbar-height: 70px;
|
|
|
|
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
--shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
|
|
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
|
|
--shadow-xl: 0 25px 50px -12px rgb(0 0 0 / 0.25);
|
|
}
|
|
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
html, body {
|
|
height: 100%;
|
|
}
|
|
|
|
body {
|
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
|
background: var(--body-bg);
|
|
color: var(--text-primary);
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
/* ============ SIDEBAR ============ */
|
|
.sidebar {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: var(--sidebar-width);
|
|
height: 100vh;
|
|
background: var(--sidebar-bg);
|
|
z-index: 1000;
|
|
transition: all 0.3s ease;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.sidebar-header {
|
|
padding: 24px 24px 20px;
|
|
border-bottom: 1px solid rgba(255,255,255,0.1);
|
|
}
|
|
|
|
.sidebar-logo {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.sidebar-logo-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
|
|
border-radius: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: white;
|
|
font-weight: 800;
|
|
font-size: 1.2rem;
|
|
}
|
|
|
|
.sidebar-logo-text {
|
|
font-weight: 700;
|
|
font-size: 1.25rem;
|
|
color: white;
|
|
}
|
|
|
|
.sidebar-logo-text span {
|
|
color: var(--secondary);
|
|
}
|
|
|
|
.sidebar-nav {
|
|
flex: 1;
|
|
padding: 20px 12px;
|
|
}
|
|
|
|
.sidebar-section {
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.sidebar-section-title {
|
|
font-size: 0.7rem;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
color: var(--text-muted);
|
|
padding: 0 12px;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.sidebar-link {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 14px;
|
|
padding: 12px 16px;
|
|
border-radius: 10px;
|
|
color: var(--sidebar-text);
|
|
text-decoration: none;
|
|
font-weight: 500;
|
|
font-size: 0.95rem;
|
|
transition: all 0.2s ease;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.sidebar-link:hover {
|
|
background: var(--sidebar-hover);
|
|
color: white;
|
|
}
|
|
|
|
.sidebar-link.active {
|
|
background: var(--sidebar-active);
|
|
color: white;
|
|
}
|
|
|
|
.sidebar-link i {
|
|
font-size: 1.2rem;
|
|
width: 24px;
|
|
text-align: center;
|
|
}
|
|
|
|
.sidebar-link .badge {
|
|
margin-left: auto;
|
|
padding: 4px 10px;
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
border-radius: 20px;
|
|
}
|
|
|
|
.sidebar-link .badge.bg-danger { background: var(--danger) !important; }
|
|
.sidebar-link .badge.bg-success { background: var(--success) !important; }
|
|
|
|
.sidebar-footer {
|
|
padding: 20px 24px;
|
|
border-top: 1px solid rgba(255,255,255,0.1);
|
|
}
|
|
|
|
.sidebar-user {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
|
|
.sidebar-user-avatar {
|
|
width: 42px;
|
|
height: 42px;
|
|
border-radius: 10px;
|
|
object-fit: cover;
|
|
border: 2px solid var(--primary);
|
|
}
|
|
|
|
.sidebar-user-info {
|
|
flex: 1;
|
|
}
|
|
|
|
.sidebar-user-name {
|
|
color: white;
|
|
font-weight: 600;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.sidebar-user-role {
|
|
color: var(--text-muted);
|
|
font-size: 0.8rem;
|
|
}
|
|
|
|
.sidebar-user-action {
|
|
color: var(--text-muted);
|
|
font-size: 1.1rem;
|
|
cursor: pointer;
|
|
transition: color 0.2s;
|
|
}
|
|
|
|
.sidebar-user-action:hover {
|
|
color: white;
|
|
}
|
|
|
|
/* ============ MAIN CONTENT ============ */
|
|
.main-wrapper {
|
|
margin-left: var(--sidebar-width);
|
|
min-height: 100vh;
|
|
transition: margin-left 0.3s ease;
|
|
}
|
|
|
|
/* ============ TOPBAR ============ */
|
|
.topbar {
|
|
position: fixed;
|
|
top: 0;
|
|
left: var(--sidebar-width);
|
|
right: 0;
|
|
height: var(--topbar-height);
|
|
background: var(--card-bg);
|
|
border-bottom: 1px solid var(--card-border);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 32px;
|
|
z-index: 100;
|
|
transition: left 0.3s ease;
|
|
}
|
|
|
|
.page-content {
|
|
padding: 32px;
|
|
overflow-x: hidden;
|
|
margin-top: var(--topbar-height);
|
|
}
|
|
|
|
.topbar-left {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 20px;
|
|
}
|
|
|
|
.topbar-toggle {
|
|
width: 40px;
|
|
height: 40px;
|
|
border: none;
|
|
background: var(--body-bg);
|
|
border-radius: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.topbar-toggle:hover {
|
|
background: var(--card-border);
|
|
}
|
|
|
|
.topbar-toggle i {
|
|
font-size: 1.2rem;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.topbar-search {
|
|
position: relative;
|
|
width: 350px;
|
|
}
|
|
|
|
.topbar-search input {
|
|
width: 100%;
|
|
height: 42px;
|
|
padding: 10px 16px 10px 44px;
|
|
border: 1px solid var(--card-border);
|
|
border-radius: 10px;
|
|
font-size: 0.9rem;
|
|
background: var(--body-bg);
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.topbar-search input:focus {
|
|
outline: none;
|
|
border-color: var(--primary);
|
|
background: white;
|
|
box-shadow: 0 0 0 3px rgba(26,95,74,0.1);
|
|
}
|
|
|
|
.topbar-search i {
|
|
position: absolute;
|
|
left: 16px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.topbar-right {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
}
|
|
|
|
.topbar-btn {
|
|
width: 42px;
|
|
height: 42px;
|
|
border: none;
|
|
background: var(--body-bg);
|
|
border-radius: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
position: relative;
|
|
}
|
|
|
|
.topbar-btn:hover {
|
|
background: var(--card-border);
|
|
}
|
|
|
|
.topbar-btn i {
|
|
font-size: 1.2rem;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.topbar-btn .badge {
|
|
position: absolute;
|
|
top: 6px;
|
|
right: 6px;
|
|
width: 18px;
|
|
height: 18px;
|
|
padding: 0;
|
|
font-size: 0.7rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.topbar-lang {
|
|
display: flex;
|
|
gap: 4px;
|
|
background: var(--body-bg);
|
|
border-radius: 10px;
|
|
padding: 4px;
|
|
}
|
|
|
|
.topbar-lang button {
|
|
padding: 8px 14px;
|
|
border: none;
|
|
background: transparent;
|
|
border-radius: 8px;
|
|
font-size: 0.85rem;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.topbar-lang button.active {
|
|
background: white;
|
|
color: var(--primary);
|
|
box-shadow: var(--shadow-sm);
|
|
}
|
|
|
|
.topbar-profile {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 6px 12px 6px 6px;
|
|
background: var(--body-bg);
|
|
border-radius: 12px;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.topbar-profile:hover {
|
|
background: var(--card-border);
|
|
}
|
|
|
|
.topbar-profile img {
|
|
width: 36px;
|
|
height: 36px;
|
|
border-radius: 8px;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.topbar-profile-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.topbar-profile-name {
|
|
font-weight: 600;
|
|
font-size: 0.85rem;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.topbar-profile-role {
|
|
font-size: 0.75rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
/* ============ PAGE CONTENT ============ */
|
|
.page-content {
|
|
padding: 32px;
|
|
}
|
|
|
|
.page-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 32px;
|
|
}
|
|
|
|
.page-title {
|
|
font-size: 1.75rem;
|
|
font-weight: 700;
|
|
color: var(--text-primary);
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.page-subtitle {
|
|
font-size: 0.9rem;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
/* ============ STATS CARDS ============ */
|
|
.stats-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
gap: 24px;
|
|
margin-bottom: 32px;
|
|
}
|
|
|
|
.stat-card {
|
|
background: var(--card-bg);
|
|
border-radius: 16px;
|
|
padding: 24px;
|
|
border: 1px solid var(--card-border);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.stat-card:hover {
|
|
transform: translateY(-4px);
|
|
box-shadow: var(--shadow-lg);
|
|
}
|
|
|
|
.stat-card-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.stat-card-icon {
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1.4rem;
|
|
}
|
|
|
|
.stat-card-icon.green { background: rgba(16,185,129,0.1); color: var(--success); }
|
|
.stat-card-icon.blue { background: rgba(59,130,246,0.1); color: var(--info); }
|
|
.stat-card-icon.orange { background: rgba(245,158,11,0.1); color: var(--warning); }
|
|
.stat-card-icon.red { background: rgba(239,68,68,0.1); color: var(--danger); }
|
|
|
|
.stat-card-trend {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
font-size: 0.85rem;
|
|
font-weight: 600;
|
|
padding: 4px 10px;
|
|
border-radius: 20px;
|
|
}
|
|
|
|
.stat-card-trend.up {
|
|
background: rgba(16,185,129,0.1);
|
|
color: var(--success);
|
|
}
|
|
|
|
.stat-card-trend.down {
|
|
background: rgba(239,68,68,0.1);
|
|
color: var(--danger);
|
|
}
|
|
|
|
.stat-card-value {
|
|
font-size: 2rem;
|
|
font-weight: 800;
|
|
color: var(--text-primary);
|
|
margin-bottom: 4px;
|
|
line-height: 1;
|
|
}
|
|
|
|
.stat-card-label {
|
|
font-size: 0.9rem;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
/* ============ CHARTS ============ */
|
|
.charts-grid {
|
|
display: grid;
|
|
grid-template-columns: 2fr 1fr;
|
|
gap: 24px;
|
|
margin-bottom: 32px;
|
|
}
|
|
|
|
.chart-card {
|
|
background: var(--card-bg);
|
|
border-radius: 16px;
|
|
padding: 24px;
|
|
border: 1px solid var(--card-border);
|
|
}
|
|
|
|
.chart-card-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.chart-card-title {
|
|
font-size: 1.1rem;
|
|
font-weight: 600;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.chart-card-actions {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
|
|
.chart-period-btn {
|
|
padding: 8px 16px;
|
|
border: 1px solid var(--card-border);
|
|
background: transparent;
|
|
border-radius: 8px;
|
|
font-size: 0.85rem;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.chart-period-btn.active, .chart-period-btn:hover {
|
|
background: var(--primary);
|
|
border-color: var(--primary);
|
|
color: white;
|
|
}
|
|
|
|
.chart-container {
|
|
position: relative;
|
|
height: 300px;
|
|
}
|
|
|
|
/* ============ TABLES ============ */
|
|
.table-card {
|
|
background: var(--card-bg);
|
|
border-radius: 16px;
|
|
border: 1px solid var(--card-border);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.table-card-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 20px 24px;
|
|
border-bottom: 1px solid var(--card-border);
|
|
}
|
|
|
|
.table-card-title {
|
|
font-size: 1.1rem;
|
|
font-weight: 600;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.table-card-action {
|
|
padding: 8px 16px;
|
|
background: var(--primary);
|
|
color: white;
|
|
border: none;
|
|
border-radius: 8px;
|
|
font-size: 0.85rem;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
text-decoration: none;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.table-card-action:hover {
|
|
background: var(--primary-dark);
|
|
color: white;
|
|
}
|
|
|
|
.table-wrapper {
|
|
padding: 0;
|
|
}
|
|
|
|
table.dataTable {
|
|
border-collapse: collapse !important;
|
|
}
|
|
|
|
table.dataTable thead th {
|
|
background: var(--body-bg);
|
|
color: var(--text-secondary);
|
|
font-weight: 600;
|
|
font-size: 0.8rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
padding: 14px 16px;
|
|
border-bottom: 1px solid var(--card-border);
|
|
}
|
|
|
|
table.dataTable tbody td {
|
|
padding: 16px;
|
|
vertical-align: middle;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
table.dataTable tbody tr:hover {
|
|
background: var(--body-bg);
|
|
}
|
|
|
|
.table-property {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
|
|
.table-property-img {
|
|
width: 50px;
|
|
height: 50px;
|
|
border-radius: 8px;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.table-property-info h6 {
|
|
font-weight: 600;
|
|
font-size: 0.9rem;
|
|
margin-bottom: 2px;
|
|
}
|
|
|
|
.table-property-info span {
|
|
font-size: 0.8rem;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.table-badge {
|
|
display: inline-flex;
|
|
padding: 5px 12px;
|
|
border-radius: 20px;
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.table-badge.new { background: rgba(59,130,246,0.1); color: var(--info); }
|
|
.table-badge.pending { background: rgba(245,158,11,0.1); color: var(--warning); }
|
|
.table-badge.completed { background: rgba(16,185,129,0.1); color: var(--success); }
|
|
.table-badge.cancelled { background: rgba(239,68,68,0.1); color: var(--danger); }
|
|
|
|
.table-user {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
.table-user-avatar {
|
|
width: 36px;
|
|
height: 36px;
|
|
border-radius: 50%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.table-user-info {
|
|
font-weight: 500;
|
|
}
|
|
|
|
.table-user-info small {
|
|
display: block;
|
|
color: var(--text-muted);
|
|
font-weight: 400;
|
|
}
|
|
|
|
.table-actions {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
|
|
.table-action-btn {
|
|
width: 32px;
|
|
height: 32px;
|
|
border: none;
|
|
background: var(--body-bg);
|
|
border-radius: 6px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.table-action-btn:hover {
|
|
background: var(--primary);
|
|
color: white;
|
|
}
|
|
|
|
.table-action-btn.danger:hover {
|
|
background: var(--danger);
|
|
}
|
|
|
|
.table-action-btn.view { color: var(--info); }
|
|
.table-action-btn.edit { color: var(--warning); }
|
|
.table-action-btn.delete { color: var(--danger); }
|
|
|
|
/* ============ ROWS GRID ============ */
|
|
.rows-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 24px;
|
|
}
|
|
|
|
.row-card {
|
|
background: var(--card-bg);
|
|
border-radius: 16px;
|
|
padding: 24px;
|
|
border: 1px solid var(--card-border);
|
|
}
|
|
|
|
.row-card-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.row-card-title {
|
|
font-size: 1rem;
|
|
font-weight: 600;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
/* ============ QUICK ACTIONS ============ */
|
|
.quick-actions {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
gap: 16px;
|
|
margin-top: 24px;
|
|
}
|
|
|
|
.quick-action {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 20px;
|
|
background: var(--body-bg);
|
|
border-radius: 12px;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
text-decoration: none;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.quick-action:hover {
|
|
background: var(--primary);
|
|
color: white;
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.quick-action-icon {
|
|
width: 48px;
|
|
height: 48px;
|
|
background: white;
|
|
border-radius: 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1.4rem;
|
|
color: var(--primary);
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.quick-action:hover .quick-action-icon {
|
|
background: rgba(255,255,255,0.2);
|
|
color: white;
|
|
}
|
|
|
|
.quick-action span {
|
|
font-size: 0.85rem;
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* ============ PAGE SECTIONS ============ */
|
|
.page-section {
|
|
display: none;
|
|
}
|
|
|
|
.page-section.active {
|
|
display: block;
|
|
}
|
|
|
|
/* ============ PROPERTY CARDS GRID ============ */
|
|
.properties-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 24px;
|
|
}
|
|
|
|
.property-admin-card {
|
|
background: var(--card-bg);
|
|
border-radius: 16px;
|
|
overflow: hidden;
|
|
border: 1px solid var(--card-border);
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.property-admin-card:hover {
|
|
transform: translateY(-4px);
|
|
box-shadow: var(--shadow-lg);
|
|
}
|
|
|
|
.property-admin-card-image {
|
|
position: relative;
|
|
height: 180px;
|
|
}
|
|
|
|
.property-admin-card-image img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.property-admin-card-badges {
|
|
position: absolute;
|
|
top: 12px;
|
|
left: 12px;
|
|
display: flex;
|
|
gap: 6px;
|
|
}
|
|
|
|
.property-admin-card-badge {
|
|
padding: 4px 10px;
|
|
border-radius: 6px;
|
|
font-size: 0.7rem;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.property-admin-card-badge.active {
|
|
background: var(--success);
|
|
color: white;
|
|
}
|
|
|
|
.property-admin-card-badge.inactive {
|
|
background: var(--text-muted);
|
|
color: white;
|
|
}
|
|
|
|
.property-admin-card-badge.urban { background: #4a90d9; color: white; }
|
|
.property-admin-card-badge.agricultural { background: var(--primary); color: white; }
|
|
.property-admin-card-badge.house { background: #9b59b6; color: white; }
|
|
.property-admin-card-badge.apartment { background: var(--secondary); color: var(--dark); }
|
|
.property-admin-card-badge.ruins { background: var(--danger); color: white; }
|
|
|
|
.property-admin-card-actions {
|
|
position: absolute;
|
|
top: 12px;
|
|
right: 12px;
|
|
display: flex;
|
|
gap: 6px;
|
|
}
|
|
|
|
.property-admin-card-action {
|
|
width: 32px;
|
|
height: 32px;
|
|
background: white;
|
|
border: none;
|
|
border-radius: 6px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
color: var(--text-secondary);
|
|
text-decoration: none;
|
|
}
|
|
|
|
.property-admin-card-action:hover {
|
|
background: var(--primary);
|
|
color: white;
|
|
}
|
|
|
|
.property-admin-card-action.delete:hover {
|
|
background: var(--danger);
|
|
}
|
|
|
|
.property-admin-card-content {
|
|
padding: 20px;
|
|
}
|
|
|
|
.property-admin-card-title {
|
|
font-size: 1rem;
|
|
font-weight: 600;
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.property-admin-card-location {
|
|
font-size: 0.85rem;
|
|
color: var(--text-muted);
|
|
margin-bottom: 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
}
|
|
|
|
.property-admin-card-stats {
|
|
display: flex;
|
|
gap: 16px;
|
|
padding-top: 12px;
|
|
border-top: 1px solid var(--card-border);
|
|
}
|
|
|
|
.property-admin-card-stat {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
font-size: 0.85rem;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.property-admin-card-stat i {
|
|
color: var(--primary);
|
|
}
|
|
|
|
.property-admin-card-price {
|
|
font-size: 1.25rem;
|
|
font-weight: 700;
|
|
color: var(--primary);
|
|
}
|
|
|
|
/* ============ MODAL ============ */
|
|
.modal-content {
|
|
border: none;
|
|
border-radius: 16px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.modal-header {
|
|
padding: 20px 24px;
|
|
border-bottom: 1px solid var(--card-border);
|
|
}
|
|
|
|
.modal-title {
|
|
font-weight: 700;
|
|
}
|
|
|
|
.modal-body {
|
|
padding: 24px;
|
|
}
|
|
|
|
.modal-footer {
|
|
padding: 16px 24px;
|
|
border-top: 1px solid var(--card-border);
|
|
}
|
|
|
|
/* ============ FORM STYLES ============ */
|
|
.form-label {
|
|
font-weight: 500;
|
|
font-size: 0.9rem;
|
|
color: var(--text-primary);
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.form-control, .form-select {
|
|
padding: 12px 16px;
|
|
border-radius: 10px;
|
|
border: 1px solid var(--card-border);
|
|
font-size: 0.9rem;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.form-control:focus, .form-select:focus {
|
|
border-color: var(--primary);
|
|
box-shadow: 0 0 0 3px rgba(26,95,74,0.1);
|
|
}
|
|
|
|
.form-check-input:checked {
|
|
background-color: var(--primary);
|
|
border-color: var(--primary);
|
|
}
|
|
|
|
.btn-primary {
|
|
background: var(--primary);
|
|
border-color: var(--primary);
|
|
padding: 12px 24px;
|
|
border-radius: 10px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
background: var(--primary-dark);
|
|
border-color: var(--primary-dark);
|
|
}
|
|
|
|
.btn-outline-primary {
|
|
border-color: var(--primary);
|
|
color: var(--primary);
|
|
padding: 12px 24px;
|
|
border-radius: 10px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.btn-outline-primary:hover {
|
|
background: var(--primary);
|
|
border-color: var(--primary);
|
|
}
|
|
|
|
/* ============ FILE UPLOAD ============ */
|
|
.file-upload {
|
|
border: 2px dashed var(--card-border);
|
|
border-radius: 12px;
|
|
padding: 40px;
|
|
text-align: center;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
background: var(--body-bg);
|
|
}
|
|
|
|
.file-upload:hover {
|
|
border-color: var(--primary);
|
|
background: rgba(26,95,74,0.05);
|
|
}
|
|
|
|
.file-upload i {
|
|
font-size: 3rem;
|
|
color: var(--text-muted);
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.file-upload h6 {
|
|
font-weight: 600;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.file-upload p {
|
|
font-size: 0.85rem;
|
|
color: var(--text-muted);
|
|
margin: 0;
|
|
}
|
|
|
|
/* ============ PAGINATION ============ */
|
|
.dataTables_wrapper .pagination {
|
|
justify-content: center;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.dataTables_wrapper .page-item.active .page-link {
|
|
background: var(--primary);
|
|
border-color: var(--primary);
|
|
}
|
|
|
|
.dataTables_wrapper .page-link {
|
|
color: var(--text-secondary);
|
|
padding: 10px 16px;
|
|
}
|
|
|
|
.dataTables_wrapper .page-link:hover {
|
|
color: var(--primary);
|
|
}
|
|
|
|
/* ============ RESPONSIVE ============ */
|
|
@media (max-width: 1400px) {
|
|
.stats-grid { grid-template-columns: repeat(2, 1fr); }
|
|
.charts-grid { grid-template-columns: 1fr; }
|
|
.rows-grid { grid-template-columns: repeat(2, 1fr); }
|
|
.properties-grid { grid-template-columns: repeat(2, 1fr); }
|
|
}
|
|
|
|
@media (max-width: 1200px) {
|
|
:root { --sidebar-width: 80px; }
|
|
|
|
.sidebar-logo-text,
|
|
.sidebar-section-title,
|
|
.sidebar-link span,
|
|
.sidebar-link .badge,
|
|
.sidebar-user-info,
|
|
.sidebar-footer { display: none; }
|
|
|
|
.sidebar-link {
|
|
justify-content: center;
|
|
padding: 14px;
|
|
}
|
|
|
|
.sidebar-link i { margin: 0; }
|
|
|
|
.sidebar-logo { justify-content: center; }
|
|
|
|
.sidebar-user { justify-content: center; }
|
|
|
|
.main-wrapper { margin-left: var(--sidebar-width); }
|
|
}
|
|
|
|
@media (max-width: 992px) {
|
|
.topbar-search { display: none; }
|
|
.topbar-lang { display: none; }
|
|
.stats-grid { grid-template-columns: 1fr; }
|
|
.properties-grid { grid-template-columns: 1fr; }
|
|
.rows-grid { grid-template-columns: 1fr; }
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.page-content { padding: 20px; }
|
|
.page-header { flex-direction: column; align-items: flex-start; gap: 16px; }
|
|
.quick-actions { grid-template-columns: repeat(2, 1fr); }
|
|
}
|
|
|
|
/* ============ ANIMATIONS ============ */
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; transform: translateY(10px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
.animate-fade-in {
|
|
animation: fadeIn 0.3s ease;
|
|
}
|
|
|
|
/* Loading Skeleton */
|
|
.skeleton {
|
|
background: linear-gradient(90deg, var(--body-bg) 25%, #e2e8f0 50%, var(--body-bg) 75%);
|
|
background-size: 200% 100%;
|
|
animation: shimmer 1.5s infinite;
|
|
}
|
|
|
|
@keyframes shimmer {
|
|
0% { background-position: -200% 0; }
|
|
100% { background-position: 200% 0; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- ============ SIDEBAR ============ -->
|
|
<aside class="sidebar" id="sidebar">
|
|
<div class="sidebar-header">
|
|
<a href="#" class="sidebar-logo">
|
|
<div class="sidebar-logo-icon">TP</div>
|
|
<span class="sidebar-logo-text">Tenerife<span>Prop</span></span>
|
|
</a>
|
|
</div>
|
|
|
|
<nav class="sidebar-nav">
|
|
<div class="sidebar-section">
|
|
<div class="sidebar-section-title" data-i18n="sidebar.main">Principal</div>
|
|
<a href="#" class="sidebar-link active" data-section="dashboard">
|
|
<i class="bi bi-grid-1x2"></i>
|
|
<span data-i18n="sidebar.dashboard">Dashboard</span>
|
|
</a>
|
|
<a href="#" class="sidebar-link" data-section="properties">
|
|
<i class="bi bi-building"></i>
|
|
<span data-i18n="sidebar.properties">Propiedades</span>
|
|
<span class="badge bg-success">9</span>
|
|
</a>
|
|
<a href="#" class="sidebar-link" data-section="leads">
|
|
<i class="bi bi-people"></i>
|
|
<span data-i18n="sidebar.leads">Leads</span>
|
|
<span class="badge bg-danger" id="leadsCount">12</span>
|
|
</a>
|
|
</div>
|
|
|
|
<div class="sidebar-section">
|
|
<div class="sidebar-section-title" data-i18n="sidebar.content">Contenido</div>
|
|
<a href="#" class="sidebar-link" data-section="testimonials">
|
|
<i class="bi bi-chat-quote"></i>
|
|
<span data-i18n="sidebar.testimonials">Testimonios</span>
|
|
</a>
|
|
<a href="#" class="sidebar-link" data-section="faq">
|
|
<i class="bi bi-question-circle"></i>
|
|
<span data-i18n="sidebar.faq">FAQ</span>
|
|
</a>
|
|
<a href="#" class="sidebar-link" data-section="services">
|
|
<i class="bi bi-briefcase"></i>
|
|
<span data-i18n="sidebar.services">Servicios</span>
|
|
</a>
|
|
<a href="#" class="sidebar-link" data-section="pages">
|
|
<i class="bi bi-file-earmark-text"></i>
|
|
<span data-i18n="sidebar.pages">Páginas</span>
|
|
</a>
|
|
</div>
|
|
|
|
<div class="sidebar-section">
|
|
<div class="sidebar-section-title" data-i18n="sidebar.analytics">Analítica</div>
|
|
<a href="#" class="sidebar-link" data-section="analytics">
|
|
<i class="bi bi-graph-up"></i>
|
|
<span data-i18n="sidebar.analytics">Estadísticas</span>
|
|
</a>
|
|
<a href="#" class="sidebar-link" data-section="traffic">
|
|
<i class="bi bi-globe"></i>
|
|
<span data-i18n="sidebar.traffic">Tráfico</span>
|
|
</a>
|
|
</div>
|
|
|
|
<div class="sidebar-section">
|
|
<div class="sidebar-section-title" data-i18n="sidebar.settings">Sistema</div>
|
|
<a href="#" class="sidebar-link" data-section="settings">
|
|
<i class="bi bi-gear"></i>
|
|
<span data-i18n="sidebar.settings">Configuración</span>
|
|
</a>
|
|
<a href="#" class="sidebar-link" data-section="users">
|
|
<i class="bi bi-person-badge"></i>
|
|
<span data-i18n="sidebar.users">Usuarios</span>
|
|
</a>
|
|
</div>
|
|
</nav>
|
|
|
|
<div class="sidebar-footer">
|
|
<div class="sidebar-user">
|
|
<img src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop" alt="Admin" class="sidebar-user-avatar">
|
|
<div class="sidebar-user-info">
|
|
<div class="sidebar-user-name">Carlos Martínez</div>
|
|
<div class="sidebar-user-role">Administrador</div>
|
|
</div>
|
|
<i class="bi bi-box-arrow-right sidebar-user-action" title="Cerrar sesión"></i>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
|
|
<!-- ============ MAIN CONTENT ============ -->
|
|
<div class="main-wrapper">
|
|
<!-- Topbar -->
|
|
<header class="topbar">
|
|
<div class="topbar-left">
|
|
<button class="topbar-toggle" id="sidebarToggle">
|
|
<i class="bi bi-list"></i>
|
|
</button>
|
|
<div class="topbar-search">
|
|
<i class="bi bi-search"></i>
|
|
<input type="text" placeholder="Buscar propiedades, leads, contenido..." id="globalSearch">
|
|
</div>
|
|
</div>
|
|
<div class="topbar-right">
|
|
<div class="topbar-lang">
|
|
<button class="active" data-lang="es">ES</button>
|
|
<button data-lang="ru">RU</button>
|
|
</div>
|
|
<button class="topbar-btn" title="Notificaciones">
|
|
<i class="bi bi-bell"></i>
|
|
<span class="badge bg-danger rounded-circle">3</span>
|
|
</button>
|
|
<button class="topbar-btn" title="Mensajes">
|
|
<i class="bi bi-chat-dots"></i>
|
|
<span class="badge bg-primary rounded-circle">5</span>
|
|
</button>
|
|
<div class="topbar-profile" id="profileDropdown">
|
|
<img src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop" alt="Admin">
|
|
<div class="topbar-profile-info">
|
|
<span class="topbar-profile-name">Carlos Martínez</span>
|
|
<span class="topbar-profile-role">Administrador</span>
|
|
</div>
|
|
<i class="bi bi-chevron-down"></i>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Page Content -->
|
|
|
|
<main class="page-content">
|
|
<div id="admin-content">
|
|
<!-- Sections loaded dynamically via JavaScript -->
|
|
</div>
|
|
</main>
|
|
</div>
|
|
<!-- ============ PROPERTY MODAL ============ -->
|
|
<div class="modal fade" id="propertyModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title"><i class="bi bi-building me-2"></i>Añadir nueva propiedad</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="row">
|
|
<div class="col-md-8">
|
|
<div class="mb-3">
|
|
<label class="form-label">Título (ES)</label>
|
|
<input type="text" class="form-control" placeholder="Terreno Urbano en Adeje">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Título (RU)</label>
|
|
<input type="text" class="form-control" placeholder="Городской участок в Адехе">
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Tipo de propiedad</label>
|
|
<select class="form-select">
|
|
<option value="">Seleccionar tipo</option>
|
|
<option value="agricultural">Terreno agrícola</option>
|
|
<option value="urban">Terreno urbano</option>
|
|
<option value="house">Casa</option>
|
|
<option value="apartment">Apartamento</option>
|
|
<option value="ruins">Ruinas</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Estado</label>
|
|
<select class="form-select">
|
|
<option value="active">Activo</option>
|
|
<option value="inactive">Inactivo</option>
|
|
<option value="sold">Vendido</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Precio (€)</label>
|
|
<input type="number" class="form-control" placeholder="385000">
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Área (m²)</label>
|
|
<input type="number" class="form-control" placeholder="2500">
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Ubicación</label>
|
|
<input type="text" class="form-control" placeholder="Adeje, Tenerife Sur">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Descripción (ES)</label>
|
|
<textarea class="form-control" rows="4" placeholder="Descripción detallada de la propiedad..."></textarea>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Descripción (RU)</label>
|
|
<textarea class="form-control" rows="4" placeholder="Подробное описание объекта..."></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="mb-3">
|
|
<label class="form-label">Coordenadas</label>
|
|
<div class="row g-2">
|
|
<div class="col-6">
|
|
<input type="text" class="form-control" placeholder="Lat">
|
|
</div>
|
|
<div class="col-6">
|
|
<input type="text" class="form-control" placeholder="Lng">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Comunicaciones</label>
|
|
<div class="form-check mb-2">
|
|
<input class="form-check-input" type="checkbox" id="modalWater" checked>
|
|
<label class="form-check-label" for="modalWater">Agua</label>
|
|
</div>
|
|
<div class="form-check mb-2">
|
|
<input class="form-check-input" type="checkbox" id="modalElectricity" checked>
|
|
<label class="form-check-label" for="modalElectricity">Electricidad</label>
|
|
</div>
|
|
<div class="form-check mb-2">
|
|
<input class="form-check-input" type="checkbox" id="modalRoad" checked>
|
|
<label class="form-check-label" for="modalRoad">Acceso rodado</label>
|
|
</div>
|
|
<div class="form-check mb-2">
|
|
<input class="form-check-input" type="checkbox" id="modalLicense">
|
|
<label class="form-check-label" for="modalLicense">Licencia de obras</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="modalSeaView">
|
|
<label class="form-check-label" for="modalSeaView">Vista al mar</label>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Badges</label>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="modalNew">
|
|
<label class="form-check-label" for="modalNew">Nuevo</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="modalExclusive">
|
|
<label class="form-check-label" for="modalExclusive">Exclusivo</label>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Imágenes</label>
|
|
<div class="file-upload" onclick="document.getElementById('imageUpload').click()">
|
|
<i class="bi bi-cloud-arrow-up"></i>
|
|
<h6>Arrastra imágenes aquí</h6>
|
|
<p>o haz clic para seleccionar</p>
|
|
</div>
|
|
<input type="file" id="imageUpload" multiple accept="image/*" style="display: none;">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancelar</button>
|
|
<button type="button" class="btn btn-primary">
|
|
<i class="bi bi-check-lg me-2"></i>Guardar propiedad
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Scripts -->
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/datatables.net@1.13.8/js/jquery.dataTables.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/datatables.net-bs5@1.13.8/js/dataTables.bootstrap5.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/moment@2.30.1/moment.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/lightpick@1.6.2/lightpick.min.js"></script>
|
|
|
|
<script>
|
|
// Dynamic section loader
|
|
const 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',
|
|
users: '/admin/users.html',
|
|
analytics: '/admin/analytics.html',
|
|
traffic: '/admin/traffic.html'
|
|
};
|
|
|
|
let currentSection = 'dashboard';
|
|
let loadedSections = {};
|
|
|
|
async function loadSection(name) {
|
|
if (loadedSections[name]) {
|
|
$(`#section-${name}`).addClass('active').siblings('.page-section').removeClass('active');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const html = await fetch(sections[name]).then(r => r.text());
|
|
$('#admin-content').append(html);
|
|
loadedSections[name] = true;
|
|
$(`#section-${name}`).addClass('active').siblings('.page-section').removeClass('active');
|
|
|
|
// Re-initialize DataTables if needed
|
|
if (name === 'leads') {
|
|
initLeadsDataTables();
|
|
}
|
|
} catch (e) {
|
|
console.error(`Failed to load section: ${name}`, e);
|
|
}
|
|
}
|
|
|
|
function initLeadsDataTables() {
|
|
if ($('#leadsTable').length && !$('#leadsTable').hasClass('dataTable')) {
|
|
$('#leadsTable').DataTable({
|
|
language: { url: 'https://cdn.datatables.net/plug-ins/1.13.8/i18n/es-ES.json' },
|
|
pageLength: 5, ordering: true, order: [[3, 'desc']],
|
|
searching: false, lengthChange: false, info: false, paging: false
|
|
});
|
|
}
|
|
if ($('#fullLeadsTable').length && !$('#fullLeadsTable').hasClass('dataTable')) {
|
|
$('#fullLeadsTable').DataTable({
|
|
language: { url: 'https://cdn.datatables.net/plug-ins/1.13.8/i18n/es-ES.json' },
|
|
pageLength: 10, ordering: true, order: [[6, 'desc']]
|
|
});
|
|
}
|
|
}
|
|
|
|
$(document).ready(function() {
|
|
// Handle browser back/forward navigation
|
|
function handleHashChange() {
|
|
const hash = window.location.hash.slice(1) || 'dashboard';
|
|
if (sections[hash]) {
|
|
$('.sidebar-link').removeClass('active');
|
|
$(`.sidebar-link[data-section="${hash}"]`).addClass('active');
|
|
loadSection(hash);
|
|
}
|
|
}
|
|
|
|
// Initial load based on URL hash
|
|
handleHashChange();
|
|
|
|
// Listen for hash changes (browser back/forward)
|
|
$(window).on('hashchange', handleHashChange);
|
|
|
|
// Sidebar navigation
|
|
$('.sidebar-link').on('click', function(e) {
|
|
e.preventDefault();
|
|
const section = $(this).data('section');
|
|
if (!section) return;
|
|
|
|
// Update URL hash
|
|
window.location.hash = section;
|
|
});
|
|
|
|
// Quick actions
|
|
$('.quick-action').on('click', function(e) {
|
|
e.preventDefault();
|
|
const section = $(this).data('section');
|
|
if (section) {
|
|
$(`.sidebar-link[data-section="${section}"]`).click();
|
|
}
|
|
});
|
|
|
|
// Sidebar toggle
|
|
$('#sidebarToggle').on('click', function() {
|
|
const sidebar = $('#sidebar');
|
|
const isCollapsed = sidebar.css('width') === '80px';
|
|
if (isCollapsed) {
|
|
sidebar.css('width', '280px');
|
|
$('.main-wrapper').css('margin-left', '280px');
|
|
$('.topbar').css('left', '280px');
|
|
$('.sidebar-logo-text, .sidebar-section-title, .sidebar-link span, .sidebar-link .badge, .sidebar-user-info, .sidebar-footer').show();
|
|
$('.sidebar-link i').css('margin-right', '14px');
|
|
$('.sidebar-logo').css('justify-content', 'flex-start');
|
|
$('.sidebar-user').css('justify-content', 'flex-start');
|
|
} else {
|
|
sidebar.css('width', '80px');
|
|
$('.main-wrapper').css('margin-left', '80px');
|
|
$('.topbar').css('left', '80px');
|
|
$('.sidebar-logo-text, .sidebar-section-title, .sidebar-link span, .sidebar-link .badge, .sidebar-user-info, .sidebar-footer').hide();
|
|
$('.sidebar-link i').css('margin-right', '0');
|
|
$('.sidebar-logo').css('justify-content', 'center');
|
|
$('.sidebar-user').css('justify-content', 'center');
|
|
}
|
|
});
|
|
});
|
|
|
|
async function logout() {
|
|
try {
|
|
await fetch('/api/auth/logout', { method: 'POST' });
|
|
localStorage.removeItem('user');
|
|
window.location.href = '/login';
|
|
} catch (e) {
|
|
window.location.href = '/login';
|
|
}
|
|
}
|
|
|
|
// Auth check
|
|
(async function checkAuth() {
|
|
try {
|
|
const res = await fetch('/api/auth/me');
|
|
const data = await res.json();
|
|
if (!data.success || !data.data) {
|
|
window.location.href = '/login';
|
|
return;
|
|
}
|
|
window.currentUser = data.data;
|
|
localStorage.setItem('user', JSON.stringify(data.data));
|
|
} catch (e) {
|
|
window.location.href = '/login';
|
|
}
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|