summaryrefslogtreecommitdiff
path: root/frontend
diff options
context:
space:
mode:
authorMatheus <matheus.guedes.mg.m@gmail.com>2025-11-04 20:29:15 -0300
committerMatheus <matheus.guedes.mg.m@gmail.com>2025-11-04 20:29:15 -0300
commitb49e9024a5bb595701b5300772b55b703b6b7856 (patch)
tree645a2fc309a0e81dc071cacc64ed302619771412 /frontend
parent48ffb49e0913dc7a94c3bdfb0c0cf69e4b8f0235 (diff)
parenta86fa3cf1e041e30ab0bae7dc6074087349dd226 (diff)
Merge branch 'master' of https://github.com/Simplesmente-O-Grupo/iot-monitoring
Diffstat (limited to 'frontend')
-rw-r--r--frontend/index.html123
-rw-r--r--frontend/script.js331
-rw-r--r--frontend/style.css253
3 files changed, 707 insertions, 0 deletions
diff --git a/frontend/index.html b/frontend/index.html
new file mode 100644
index 0000000..cd31566
--- /dev/null
+++ b/frontend/index.html
@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<html lang="pt-br">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Dashboard</title>
+
+ <link rel="preconnect" href="https://fonts.googleapis.com">
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+ <link href="https://fonts.googleapis.com/css2?family=Sora:wght@300;400;600;700&display=swap" rel="stylesheet">
+
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
+
+ <link rel="stylesheet" href="style.css">
+</head>
+<body>
+
+ <div class="dashboard-container">
+
+ <aside class="sidebar">
+ <header class="sidebar-header">
+ <h2>Dashboard</h2>
+ <span>Iot Monitoration</span>
+ </header>
+
+ <nav class="sidebar-nav">
+ <h3><i class="fas fa-satellite-dish"></i> Current Data</h3>
+
+ <div class="data-widget">
+ <div class="widget-icon icon-temp">
+ <i class="fas fa-thermometer-half"></i>
+ </div>
+ <div class="widget-info">
+ <span id="currentTemp">--.- °C</span>
+ <p>Temperature</p>
+ </div>
+ </div>
+
+ <div class="data-widget">
+ <div class="widget-icon icon-humidity">
+ <i class="fas fa-tint"></i>
+ </div>
+ <div class="widget-info">
+ <span id="currentHumidity">-- %</span>
+ <p>Humidity</p>
+ </div>
+ </div>
+
+ <div class="data-widget">
+ <div class="widget-icon icon-wind">
+ <i class="fas fa-wind"></i>
+ </div>
+ <div class="widget-info">
+ <span id="currentWind">--.- km/h</span>
+ <p>Wild</p>
+ </div>
+ </div>
+
+ <div class="data-widget">
+ <div class="widget-icon icon-pressure">
+ <i class="fas fa-tachometer-alt"></i>
+ </div>
+ <div class="widget-info">
+ <span id="currentPressure">---- hPa</span>
+ <p>Pressure</p>
+ </div>
+ </div>
+
+ </nav>
+
+ <footer class="sidebar-footer">
+ <button id="toggleUpdatesBtn">
+ <i class="fas fa-cog"></i> <span>Settings</span>
+ </button>
+ </footer>
+ </aside>
+
+ <main class="main-content">
+ <h1>Monitoring Dashboard</h1>
+
+ <section class="charts-grid">
+
+ <div class="chart-card large">
+ <h3>Temperature and Pressure (Last 30s)</h3>
+ <canvas id="graficoTempPressao"></canvas>
+ </div>
+
+ <div class="chart-card">
+ <h3>Humidity</h3>
+ <div class="gauge-container">
+ <canvas id="graficoUmidade"></canvas>
+ <div id="umidadeText" class="gauge-text">--%</div>
+ </div>
+ </div>
+
+ <div class="chart-card">
+ <h3>UV Indice</h3>
+ <div class="gauge-container">
+ <canvas id="graficoUv"></canvas>
+ <div id="uvText" class="gauge-text">--</div>
+ </div>
+ </div>
+
+ <div class="chart-card">
+ <h3>Wild Speed</h3>
+ <canvas id="graficoVento"></canvas>
+ </div>
+
+ <div class="chart-card">
+ <h3>Visibility</h3>
+ <canvas id="graficoVisibilidade"></canvas>
+ </div>
+
+ </section>
+ </main>
+ </div>
+
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns"></script>
+
+ <script src="script.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/frontend/script.js b/frontend/script.js
new file mode 100644
index 0000000..5a3e77c
--- /dev/null
+++ b/frontend/script.js
@@ -0,0 +1,331 @@
+document.addEventListener("DOMContentLoaded", () => {
+ // --- Configurações Globais ---
+ const updateInterval = 2000; // 2 segundos
+ let intervalId = null;
+ let isPaused = false;
+ const maxDataPoints = 15; // 15 pontos * 2s = 30s de dados
+
+ // --- Elementos do DOM ---
+ const toggleUpdatesBtn = document.getElementById('toggleUpdatesBtn');
+ const statusText = document.getElementById('statusText');
+
+ // Widgets da Sidebar
+ const currentTempSpan = document.getElementById('currentTemp');
+ const currentHumiditySpan = document.getElementById('currentHumidity');
+ const currentWindSpan = document.getElementById('currentWind');
+ const currentPressureSpan = document.getElementById('currentPressure');
+
+ // Texto dos Medidores
+ const umidadeText = document.getElementById('umidadeText');
+ const uvText = document.getElementById('uvText');
+
+ // --- Configurações Globais do Chart.js para o Tema Dark ---
+ Chart.defaults.color = '#aaaaaa'; // Cor da fonte (eixos, legendas)
+ Chart.defaults.borderColor = 'rgba(255, 255, 255, 0.1)'; // Cor das linhas do grid
+
+ // --- Helper para Gradiente ---
+ function createChartGradient(ctx, color) {
+ const gradient = ctx.createLinearGradient(0, 0, 0, 400);
+
+ // Pega a cor das variáveis CSS. Ex: 'var(--temp-color)'
+ let colorHex = getComputedStyle(document.documentElement).getPropertyValue(color.match(/\((.*?)\)/)[1]).trim();
+
+ // Converte hex para rgba
+ const r = parseInt(colorHex.slice(1, 3), 16);
+ const g = parseInt(colorHex.slice(3, 5), 16);
+ const b = parseInt(colorHex.slice(5, 7), 16);
+
+ gradient.addColorStop(0, `rgba(${r}, ${g}, ${b}, 0.6)`);
+ gradient.addColorStop(1, `rgba(${r}, ${g}, ${b}, 0.05)`);
+ return gradient;
+ }
+
+ // --- PARTE 1: SIMULAÇÃO DA API ---
+ // QUANDO FOR USAR A API REAL, VOCÊ VAI APAGAR ESTA FUNÇÃO
+ function simularApiDaEstacao() {
+ const temperatura = (Math.random() * 10 + 15).toFixed(1); // 15.0 - 25.0
+ const umidade = (Math.random() * 30 + 50).toFixed(0); // 50 - 80
+ const vento = (Math.random() * 15 + 5).toFixed(1); // 5.0 - 20.0
+ const pressao = (Math.random() * 20 + 1000).toFixed(0); // 1000 - 1020
+ const uv = Math.floor(Math.random() * 11); // 0 - 10
+ const visibilidade = (Math.random() * 15 + 5).toFixed(1); // 5.0 - 20.0
+
+ return {
+ timestamp: new Date(),
+ temperatura: parseFloat(temperatura),
+ umidade: parseInt(umidade),
+ vento: parseFloat(vento),
+ pressao: parseInt(pressao),
+ uv: parseInt(uv),
+ visibilidade: parseFloat(visibilidade)
+ };
+ }
+
+ // API REAL
+ /*
+ async function fetchApiReal() {
+ const url = 'http://URL_DA_SUA_API_PYTHON/dados';
+ try {
+ const response = await fetch(url);
+ if (!response.ok) {
+ throw new Error(`Erro na API: ${response.statusText}`);
+ }
+ const data = await response.json();
+
+ // Adiciona o timestamp, já que a API pode não mandar
+ data.timestamp = new Date();
+ return data;
+
+ } catch (error) {
+ console.error("Falha ao buscar dados da API", error);
+ // Retorna nulo para não quebrar o dashboard
+ return null;
+ }
+ }
+ */
+
+
+ // --- PARTE 2: INICIALIZAÇÃO DOS GRÁFICOS ---
+
+// // 1. Gráfico de Linha (Temperatura & Pressão) - 2 eixos Y
+// const ctxTempPressao = document.getElementById('graficoTempPressao').getContext('2d');
+// const graficoTempPressao = new Chart(ctxTempPressao, {
+// type: 'line',
+// data: {
+// labels: [],
+// datasets: [
+// {
+// label: 'Temperatura',
+// data: [],
+// borderColor: 'var(--temp-color)',
+// backgroundColor: createChartGradient(ctxTempPressao, 'var(--temp-color)'),
+// fill: true,
+// tension: 0.4,
+// yAxisID: 'yTemp' // Associa ao eixo Y da temperatura
+// },
+// {
+// label: 'Pressão',
+// data: [],
+// borderColor: 'var(--pressure-color)',
+// backgroundColor: createChartGradient(ctxTempPressao, 'var(--pressure-color)'),
+// fill: true,
+// tension: 0.4,
+// yAxisID: 'yPressure' // Associa ao eixo Y da pressão
+// }
+// ]
+// },
+// options: {
+// responsive: true,
+// maintainAspectRatio: false,
+// interaction: { mode: 'index', intersect: false },
+// scales: {
+// x: {
+// type: 'time',
+// time: { unit: 'second', displayFormats: { second: 'HH:mm:ss' } },
+// ticks: { maxRotation: 0, autoSkip: true, maxTicksLimit: 6 }
+// },
+// yTemp: { // Eixo Y da Esquerda (Temperatura)
+// type: 'linear',
+// position: 'left',
+// grid: { drawOnChartArea: false }, // Remove grid deste eixo
+// ticks: { callback: value => `${value} °C` }
+// },
+// yPressure: { // Eixo Y da Direita (Pressão)
+// type: 'linear',
+// position: 'right',
+// ticks: { callback: value => `${value} hPa` }
+// }
+// },
+// plugins: {
+// legend: { position: 'top' },
+// tooltip: {
+// backgroundColor: '#000',
+// titleFont: { weight: 'bold' },
+// bodySpacing: 4,
+// padding: 10,
+// borderColor: 'var(--border-color)',
+// borderWidth: 1
+// }
+// }
+// }
+// });
+
+// // 2. Medidor de Umidade (Doughnut)
+// const graficoUmidade = new Chart(document.getElementById('graficoUmidade').getContext('2d'), {
+// type: 'doughnut',
+// data: {
+// datasets: [{
+// data: [0, 100], // Valor, Restante
+// backgroundColor: ['var(--humidity-color)', '#2f2f2f'], // Cor da umidade, Cor do fundo
+// borderWidth: 0,
+// borderRadius: 5
+// }]
+// },
+// options: {
+// responsive: true,
+// maintainAspectRatio: false,
+// circumference: 270, // Arco de 270 graus
+// rotation: 225, // Começa no canto inferior esquerdo
+// cutout: '80%',
+// plugins: { legend: { display: false }, tooltip: { enabled: false } }
+// }
+// });
+
+// // 3. Medidor de UV (Doughnut)
+// const graficoUv = new Chart(document.getElementById('graficoUv').getContext('2d'), {
+// type: 'doughnut',
+// data: {
+// datasets: [{
+// data: [0, 12], // Valor, Max (escala UV vai ~12)
+// backgroundColor: ['var(--uv-color)', '#2f2f2f'], // Cor UV, Cor do fundo
+// borderWidth: 0,
+// borderRadius: 5
+// }]
+// },
+// options: {
+// responsive: true,
+// maintainAspectRatio: false,
+// circumference: 270,
+// rotation: 225,
+// cutout: '80%',
+// plugins: { legend: { display: false }, tooltip: { enabled: false } }
+// }
+// });
+
+// // 4. Gráfico de Vento (Barra Vertical)
+// const graficoVento = new Chart(document.getElementById('graficoVento').getContext('2d'), {
+// type: 'bar',
+// data: {
+// labels: ['Vento'],
+// datasets: [{
+// label: 'km/h',
+// data: [0],
+// backgroundColor: 'var(--accent-color)', // Cor de destaque
+// borderRadius: 4
+// }]
+// },
+// options: {
+// responsive: true,
+// maintainAspectRatio: false,
+// scales: {
+// x: { display: false },
+// y: { beginAtZero: true, max: 30 }
+// },
+// plugins: { legend: { display: false } }
+// }
+// });
+
+// // 5. Gráfico de Visibilidade (Barra Horizontal)
+// const graficoVisibilidade = new Chart(document.getElementById('graficoVisibilidade').getContext('2d'), {
+// type: 'bar',
+// data: {
+// labels: ['Visibilidade'],
+// datasets: [{
+// label: 'km',
+// data: [0],
+// backgroundColor: '#9c27b0', // Roxo
+// borderRadius: 4
+// }]
+// },
+// options: {
+// indexAxis: 'y', // Barra horizontal
+// responsive: true,
+// maintainAspectRatio: false,
+// scales: {
+// y: { display: false },
+// x: { beginAtZero: true, max: 25 }
+// },
+// plugins: { legend: { display: false } }
+// }
+// });
+
+
+// // --- PARTE 3: FUNÇÃO DE ATUALIZAÇÃO ---
+
+// // Esta função será chamada a cada X segundos
+// async function atualizarDashboard() {
+
+// // Mude aqui para usar a API real
+// // const dados = await fetchApiReal();
+// const dados = simularApiDaEstacao(); // Usando simulação por enquanto
+
+// // Se a API falhar, 'dados' será nulo. Pulamos a atualização.
+// if (!dados) {
+// return;
+// }
+
+// // Atualiza Widgets da Sidebar
+// currentTempSpan.textContent = `${dados.temperatura.toFixed(1)} °C`;
+// currentHumiditySpan.textContent = `${dados.umidade} %`;
+// currentWindSpan.textContent = `${dados.vento.toFixed(1)} km/h`;
+// currentPressureSpan.textContent = `${dados.pressao} hPa`;
+
+// // Atualiza Gráfico de Linha (Temp & Pressão)
+// const labels = graficoTempPressao.data.labels;
+// const tempAtivos = graficoTempPressao.data.datasets[0].data;
+// const pressAtivos = graficoTempPressao.data.datasets[1].data;
+
+// labels.push(dados.timestamp);
+// tempAtivos.push(dados.temperatura);
+// pressAtivos.push(dados.pressao);
+
+// if (labels.length > maxDataPoints) {
+// labels.shift();
+// tempAtivos.shift();
+// pressAtivos.shift();
+// }
+// graficoTempPressao.update();
+
+// // Atualiza Medidor de Umidade
+// umidadeText.textContent = `${dados.umidade}%`;
+// graficoUmidade.data.datasets[0].data = [dados.umidade, 100 - dados.umidade];
+// graficoUmidade.update();
+
+// // Atualiza Medidor de UV (com cor dinâmica)
+// let uvColor;
+// if (dados.uv <= 2) { uvColor = '#4caf50'; } // Verde
+// else if (dados.uv <= 5) { uvColor = '#fdd835'; } // Amarelo
+// else if (dados.uv <= 7) { uvColor = '#ff9800'; } // Laranja
+// else { uvColor = '#f44336'; } // Vermelho
+
+// uvText.textContent = dados.uv;
+// graficoUv.data.datasets[0].data = [dados.uv, 12 - dados.uv];
+// graficoUv.data.datasets[0].backgroundColor[0] = uvColor;
+// graficoUv.update();
+
+// // Atualiza Gráfico de Vento
+// graficoVento.data.datasets[0].data = [dados.vento];
+// graficoVento.update();
+
+// // Atualiza Gráfico de Visibilidade
+// graficoVisibilidade.data.datasets[0].data = [dados.visibilidade];
+// graficoVisibilidade.update();
+// }
+
+// // --- PARTE 4: CONTROLE DE ATUALIZAÇÃO ---
+
+// toggleUpdatesBtn.addEventListener('click', () => {
+// isPaused = !isPaused;
+// if (isPaused) {
+// clearInterval(intervalId);
+// intervalId = null;
+// toggleUpdatesBtn.innerHTML = '<i class="fas fa-play"></i> <span>Retomar</span>';
+// statusText.textContent = 'Pausado';
+// statusText.style.color = '#ff9800';
+// } else {
+// startUpdates();
+// toggleUpdatesBtn.innerHTML = '<i class="fas fa-pause"></i> <span>Pausar</span>';
+// statusText.textContent = 'Monitorando...';
+// statusText.style.color = 'var(--accent-color)';
+// }
+// });
+
+// function startUpdates() {
+// if (intervalId === null) {
+// atualizarDashboard(); // Roda uma vez imediatamente
+// intervalId = setInterval(atualizarDashboard, updateInterval);
+// }
+// }
+
+// startUpdates(); // Inicia ao carregar
+// }); \ No newline at end of file
diff --git a/frontend/style.css b/frontend/style.css
new file mode 100644
index 0000000..be965cb
--- /dev/null
+++ b/frontend/style.css
@@ -0,0 +1,253 @@
+/* --- Variáveis de Cor (Paleta "Neon Tech") --- */
+:root {
+ --bg-color: #121212; /* Fundo principal (preto) */
+ --card-color: #1e1e1e; /* Fundo dos cards (grafite) */
+ --border-color: #2f2f2f; /* Bordas sutis */
+ --text-primary: #e0e0e0; /* Texto principal (cinza claro) */
+ --text-secondary: #aaaaaa; /* Texto secundário (cinza) */
+ --accent-color: #00ff9a; /* Destaque (verde neon) */
+ --accent-color-dark: #00b36e;
+ --accent-color-transparent: rgba(0, 255, 154, 0.1);
+
+ --temp-color: #ff5252; /* Vermelho para temperatura */
+ --pressure-color: #448aff; /* Azul para pressão */
+ --humidity-color: #29b6f6; /* Ciano para umidade */
+ --uv-color: #fdd835; /* Amarelo para UV */
+
+ --font-family: 'Sora', 'Segoe UI', system-ui, sans-serif;
+}
+
+/* --- Resets e Estilos Globais --- */
+* {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ font-family: var(--font-family);
+ background-color: var(--bg-color);
+ color: var(--text-primary);
+ display: flex;
+ height: 100vh;
+ overflow: hidden; /* Impede o scroll da página inteira */
+}
+
+h1, h2, h3 {
+ font-weight: 600;
+}
+
+/* --- Layout Principal --- */
+.dashboard-container {
+ display: flex;
+ width: 100%;
+ height: 100%;
+}
+
+/* --- Barra Lateral (Sidebar) --- */
+.sidebar {
+ width: 280px;
+ flex-shrink: 0;
+ background-color: var(--card-color);
+ border-right: 1px solid var(--border-color);
+ display: flex;
+ flex-direction: column;
+ padding: 20px;
+ overflow-y: auto; /* Permite scroll na sidebar se precisar */
+}
+
+.sidebar-header {
+ text-align: center;
+ margin-bottom: 30px;
+ border-bottom: 1px solid var(--border-color);
+ padding-bottom: 20px;
+}
+.sidebar-header h2 {
+ color: var(--accent-color);
+ font-weight: 700;
+ letter-spacing: 1px;
+}
+.sidebar-header span {
+ font-size: 0.9rem;
+ color: var(--text-secondary);
+}
+
+.sidebar-nav {
+ flex-grow: 1;
+}
+.sidebar-nav h3 {
+ color: var(--text-secondary);
+ font-size: 0.8rem;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ margin-bottom: 15px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+/* Widgets de Dados Atuais */
+.data-widget {
+ display: flex;
+ align-items: center;
+ margin-bottom: 20px;
+ background-color: var(--bg-color);
+ padding: 12px;
+ border-radius: 8px;
+ border: 1px solid var(--border-color);
+ transition: border-color 0.3s ease;
+}
+.data-widget:hover {
+ border-color: var(--accent-color);
+}
+
+.widget-icon {
+ font-size: 1.5rem;
+ margin-right: 15px;
+ padding: 10px;
+ border-radius: 50%;
+ width: 48px;
+ height: 48px;
+ display: grid;
+ place-items: center;
+}
+.widget-info span {
+ font-size: 1.4rem;
+ font-weight: 600;
+ color: var(--text-primary);
+}
+.widget-info p {
+ font-size: 0.9rem;
+ color: var(--text-secondary);
+}
+
+/* Cores dos ícones dos widgets */
+.icon-temp { background-color: rgba(255, 82, 82, 0.1); color: var(--temp-color); }
+.icon-humidity { background-color: rgba(41, 182, 246, 0.1); color: var(--humidity-color); }
+.icon-wind { background-color: rgba(0, 255, 154, 0.1); color: var(--accent-color); }
+.icon-pressure { background-color: rgba(68, 138, 255, 0.1); color: var(--pressure-color); }
+
+.sidebar-footer {
+ border-top: 1px solid var(--border-color);
+ padding-top: 20px;
+ text-align: center;
+}
+.sidebar-footer button {
+ background-color: var(--accent-color);
+ color: #000;
+ border: none;
+ padding: 12px 20px;
+ border-radius: 5px;
+ cursor: pointer;
+ font-family: var(--font-family);
+ font-weight: 600;
+ font-size: 1rem;
+ width: 100%;
+ margin-bottom: 10px;
+ transition: background-color 0.3s ease;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+}
+.sidebar-footer button:hover {
+ background-color: var(--accent-color-dark);
+}
+.sidebar-footer p {
+ font-size: 0.8rem;
+ color: var(--text-secondary);
+}
+#statusText {
+ color: var(--accent-color);
+ font-weight: 600;
+}
+
+/* --- Conteúdo Principal (Gráficos) --- */
+.main-content {
+ flex-grow: 1;
+ padding: 30px;
+ overflow-y: auto; /* Permite scroll se os gráficos não couberem */
+}
+
+.main-content h1 {
+ font-size: 2rem;
+ margin-bottom: 30px;
+ color: var(--text-primary);
+}
+
+.charts-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: 20px;
+}
+
+.chart-card {
+ background-color: var(--card-color);
+ border: 1px solid var(--border-color);
+ border-radius: 8px;
+ padding: 20px;
+ min-height: 350px;
+ display: flex;
+ flex-direction: column;
+
+ /* A CORREÇÃO VITAL:
+ Isso "prende" o canvas do Chart.js dentro do card,
+ impedindo que ele tente criar um canvas infinito
+ quando executado no Live Server.
+ */
+ position: relative;
+}
+.chart-card.large {
+ grid-column: span 2; /* Faz o gráfico ocupar 2 colunas */
+}
+.chart-card h3 {
+ margin-bottom: 20px;
+ text-align: center;
+ color: var(--text-secondary);
+ font-weight: 400;
+ font-size: 1rem;
+}
+
+/* Contêiner para Medidores (Gauge) */
+.gauge-container {
+ position: relative;
+ flex-grow: 1;
+ display: grid;
+ place-items: center;
+}
+.gauge-text {
+ position: absolute;
+ font-size: 2.5rem;
+ font-weight: 700;
+ color: var(--text-primary);
+}
+#uvText.gauge-text {
+ font-size: 3rem; /* UV é só um número, pode ser maior */
+}
+
+/* --- Responsividade --- */
+@media (max-width: 1200px) {
+ .chart-card.large {
+ grid-column: span 1; /* Em telas menores, o gráfico largo ocupa 1 coluna */
+ }
+}
+@media (max-width: 900px) {
+ body {
+ height: auto;
+ overflow: auto; /* Permite scroll em telas pequenas */
+ }
+ .dashboard-container {
+ flex-direction: column;
+ }
+ .sidebar {
+ width: 100%;
+ height: auto;
+ border-right: none;
+ border-bottom: 1px solid var(--border-color);
+ overflow-y: visible;
+ }
+ .main-content {
+ height: auto;
+ overflow-y: visible;
+ }
+} \ No newline at end of file