This commit is contained in:
Flatlogic Bot 2025-10-29 15:00:42 +00:00
parent a0c63edc92
commit 07e06596db
6 changed files with 477 additions and 219 deletions

View File

@ -1,8 +1,235 @@
/* General Body & Layout */
body { body {
font-family: 'Inter', sans-serif; font-family: 'Inter', sans-serif;
background-color: #f8f9fa; background-color: #f8f9fa;
transition: margin-left .3s;
} }
.wrapper {
display: flex;
width: 100%;
}
/* Sidebar Styles */
#sidebar {
width: 280px;
position: fixed;
top: 0;
left: 0;
height: 100vh;
z-index: 999;
background: #4C5958;
color: #fff;
transition: all 0.3s;
overflow-y: auto;
padding-bottom: 20px;
}
#sidebar.mini {
width: 80px;
}
#sidebar.mini .sidebar-header h3,
#sidebar.mini .menu-item-text,
#sidebar.mini .profile-text,
#sidebar.mini .dropdown-toggle::after {
display: none;
}
#sidebar.mini .menu-item .lucide {
margin-right: 0;
}
#sidebar.mini .profile-section .lucide-user-circle {
margin: 0 auto;
}
.sidebar-header {
padding: 20px;
background: #4C5958;
text-align: center;
border-bottom: 1px solid #5a6867;
}
.sidebar-header h3 {
color: white;
font-weight: 700;
margin-bottom: 0;
font-size: 1.5rem;
white-space: nowrap;
}
/* Profile Section */
.profile-section {
padding: 15px 20px;
border-bottom: 1px solid #5a6867;
color: white;
}
.profile-section .dropdown-toggle {
color: white;
text-decoration: none;
display: flex;
align-items: center;
padding: 5px 0;
}
.profile-section .lucide-user-circle {
width: 40px;
height: 40px;
margin-right: 10px;
}
.profile-section .profile-text span {
display: block;
white-space: nowrap;
}
.profile-section .profile-text .username {
font-weight: bold;
}
.profile-section .dropdown-menu {
background-color: #3f4a49;
border: none;
}
.profile-section .dropdown-item {
color: #e0e0e0;
}
.profile-section .dropdown-item:hover {
background-color: #5a6867;
color: white;
}
/* Menu List Styles */
.menu-list {
list-style: none;
padding: 10px 0;
margin: 0;
}
.menu-list a {
display: flex;
align-items: center;
padding: 12px 20px;
text-decoration: none;
transition: background 0.2s;
font-size: 0.95rem;
white-space: nowrap;
}
.menu-list .lucide {
margin-right: 15px;
width: 20px;
height: 20px;
flex-shrink: 0;
}
/* Top Level Items */
.menu-section > a {
color: #DBF227;
font-weight: 500;
letter-spacing: 0.5px;
}
.menu-section > a:hover {
background: #3f4a49;
}
/* Sub Items (Level 2) */
.sub-menu {
list-style: none;
padding-left: 0;
background: rgba(0,0,0,0.1);
}
.sub-menu a {
/* NOTE: Using white for better contrast against the dark sidebar.
The requested #10403B is too dark for this background. */
color: #FFFFFF;
font-size: 0.9rem;
padding-left: 35px;
}
.sub-menu a:hover {
background: #3f4a49;
}
.sub-menu .lucide {
width: 18px;
height: 18px;
}
/* Sub-Sub Items (Level 3) */
.sub-sub-menu {
list-style: none;
padding-left: 0;
background: rgba(0,0,0,0.15);
}
.sub-sub-menu a {
color: #e0e0e0; /* Slightly dimmer white */
font-size: 0.85rem;
padding-left: 55px;
}
.sub-sub-menu a:hover {
background: #3f4a49;
}
/* Dropdown Arrow for collapsible menus */
.dropdown-toggle::after {
display: inline-block;
margin-left: auto;
vertical-align: .255em;
content: "";
border-top: .3em solid;
border-right: .3em solid transparent;
border-bottom: 0;
border-left: .3em solid transparent;
transition: transform .2s ease-in-out;
}
.dropdown-toggle[aria-expanded="true"]::after {
transform: rotate(-180deg);
}
/* Badge for notifications */
.badge-notification {
background-color: #dc3545;
color: white;
border-radius: 50%;
padding: 0.2em 0.5em;
font-size: 0.7rem;
margin-left: auto;
margin-right: 10px;
}
/* Main Content Area */
#content {
width: 100%;
padding: 20px;
min-height: 100vh;
transition: all 0.3s;
margin-left: 280px;
}
#content.full-width {
margin-left: 80px;
}
.content-header {
display: flex;
align-items: center;
margin-bottom: 20px;
}
#sidebar-toggle {
background: none;
border: none;
font-size: 1.5rem;
color: #333;
cursor: pointer;
}
/* Login Form Styles (keep existing) */
.login-container { .login-container {
min-height: 100vh; min-height: 100vh;
display: flex; display: flex;
@ -19,7 +246,21 @@ body {
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.1); box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.1);
} }
.navbar-brand-logo { /* Responsive */
font-weight: 700; @media (max-width: 768px) {
color: #0D6EFD; /* Bootstrap Primary */ #sidebar {
} left: -280px;
}
#sidebar.active {
left: 0;
}
#content {
margin-left: 0;
}
#content.full-width {
margin-left: 0;
}
body.sidebar-open {
overflow: hidden;
}
}

View File

@ -1,3 +1,23 @@
<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/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Initialize Lucide icons
lucide.createIcons();
const sidebarToggle = document.getElementById('sidebar-toggle');
const sidebar = document.getElementById('sidebar');
const content = document.getElementById('content');
if (sidebarToggle && sidebar && content) {
sidebarToggle.addEventListener('click', function() {
// This handles both mobile (active) and desktop (mini) states
// The CSS media queries will apply the correct styles.
sidebar.classList.toggle('active');
sidebar.classList.toggle('mini');
content.classList.toggle('full-width');
});
}
});
</script>
</body> </body>
</html> </html>

View File

@ -1,10 +1,7 @@
<?php <?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// Buscar o nome do usuário se estiver logado // Buscar o nome do usuário se estiver logado
$user_name = ''; $user_name = '';
$first_name = '';
if (isset($_SESSION['user_id'])) { if (isset($_SESSION['user_id'])) {
try { try {
$pdo = db(); $pdo = db();
@ -12,69 +9,184 @@ if (isset($_SESSION['user_id'])) {
$stmt->execute(['id' => $_SESSION['user_id']]); $stmt->execute(['id' => $_SESSION['user_id']]);
$user = $stmt->fetch(PDO::FETCH_ASSOC); $user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user) { if ($user) {
// Pega o primeiro nome para uma saudação mais curta
$first_name = explode(' ', $user['name'])[0];
$user_name = $user['name']; $user_name = $user['name'];
} }
} catch (PDOException $e) { } catch (PDOException $e) {
// Em caso de erro, o nome fica em branco // Em caso de erro, o nome fica em branco
} }
} }
// Placeholder for unclassified expenses count
$unclassifiedCount = 5; // Example value
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="pt-BR"> <html lang="pt-BR">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Galilei Finance</title> <title>Concilia Fácil</title>
<meta name="description" content="Software de controle financeiro familiar para transformar despesas em investimentos."> <meta name="description" content="Software de controle financeiro familiar para transformar despesas em investimentos.">
<meta name="keywords" content="controle financeiro, finanças pessoais, orçamento familiar, investimentos, economizar dinheiro, gestão de despesas, app de finanças, Built with Flatlogic Generator"> <meta name="keywords" content="controle financeiro, finanças pessoais, orçamento familiar, investimentos, economizar dinheiro, gestão de despesas, app de finanças, Built with Flatlogic Generator">
<meta property="og:title" content="Galilei Finance"> <meta property="og:title" content="Concilia Fácil">
<meta property="og:description" content="Software de controle financeiro familiar para transformar despesas em investimentos."> <meta property="og:description" content="Software de controle financeiro familiar para transformar despesas em investimentos.">
<meta property="og:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? '', ENT_QUOTES, 'UTF-8'); ?>"> <meta property="og:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? '', ENT_QUOTES, 'UTF-8'); ?>">
<meta name="twitter:card" content="summary_large_image"> <meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? '', ENT_QUOTES, 'UTF-8'); ?>"> <meta name="twitter:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? '', ENT_QUOTES, 'UTF-8'); ?>">
<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@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>"> <link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<!-- Lucide Icons -->
<script src="https://unpkg.com/lucide@latest"></script>
</head> </head>
<body> <body>
<nav class="navbar navbar-expand-lg navbar-light bg-light"> <div class="wrapper">
<div class="container"> <!-- Sidebar -->
<a class="navbar-brand navbar-brand-logo" href="index.php"> <?php if (isset($_SESSION['user_id'])): ?>
<i class="bi bi-safe me-2"></i> <nav id="sidebar">
Galilei Finance <div class="sidebar-header">
</a> <h3>Concilia Fácil</h3>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> </div>
<span class="navbar-toggler-icon"></span>
</button> <div class="profile-section dropdown">
<div class="collapse navbar-collapse" id="navbarNav"> <a href="#" class="dropdown-toggle" id="dropdownUser1" data-bs-toggle="dropdown" aria-expanded="false">
<?php if (isset($_SESSION['user_id'])): ?> <i data-lucide="user-circle"></i>
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <div class="profile-text">
<li class="nav-item"> <span class="username"><?php echo htmlspecialchars($first_name); ?></span>
<a class="nav-link" href="index.php">Dashboard</a> <span>Meu Perfil</span>
</div>
</a>
<ul class="dropdown-menu dropdown-menu-dark" aria-labelledby="dropdownUser1">
<li><a class="dropdown-item" href="profile.php">Ver Perfil</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="logout.php">Sair</a></li>
</ul>
</div>
<ul class="list-unstyled menu-list">
<!-- Cadastros Básicos -->
<li class="menu-section">
<a href="#cadastrosSubmenu" data-bs-toggle="collapse" aria-expanded="false" class="dropdown-toggle">
<i data-lucide="folder-tree"></i>
<span class="menu-item-text">Cadastros Básicos</span>
</a>
<ul class="collapse list-unstyled sub-menu" id="cadastrosSubmenu">
<!-- Plano de Contas -->
<li>
<a href="#planoContasSubmenu" data-bs-toggle="collapse" aria-expanded="false" class="dropdown-toggle">
<i data-lucide="book-text"></i>
<span class="menu-item-text">Plano de Contas</span>
</a>
<ul class="collapse list-unstyled sub-sub-menu" id="planoContasSubmenu">
<li><a href="#"><span class="menu-item-text">Macro Áreas</span></a></li>
<li><a href="#"><span class="menu-item-text">Categorias</span></a></li>
<li><a href="#"><span class="menu-item-text">Centro de Custo</span></a></li>
<li><a href="#"><span class="menu-item-text">Alocação do Plano</span></a></li>
</ul>
</li> </li>
<li class="nav-item"> <!-- Bancos -->
<a class="nav-link" href="expenses.php">Despesas</a> <li>
<a href="#bancosSubmenu" data-bs-toggle="collapse" aria-expanded="false" class="dropdown-toggle">
<i data-lucide="building-2"></i>
<span class="menu-item-text">Bancos</span>
</a>
<ul class="collapse list-unstyled sub-sub-menu" id="bancosSubmenu">
<li><a href="#"><span class="menu-item-text">Bancos</span></a></li>
<li><a href="#"><span class="menu-item-text">Tipo de Conta</span></a></li>
<li><a href="#"><span class="menu-item-text">Contas Bancárias</span></a></li>
<li><a href="#"><span class="menu-item-text">Status de Pagamento</span></a></li>
<li><a href="#"><span class="menu-item-text">Portabilidade</span></a></li>
</ul>
</li> </li>
<li class="nav-item"> <!-- Famílias -->
<a class="nav-link" href="budgets.php">Orçamentos</a> <li>
<a href="#familiasSubmenu" data-bs-toggle="collapse" aria-expanded="false" class="dropdown-toggle">
<i data-lucide="users"></i>
<span class="menu-item-text">Famílias</span>
</a>
<ul class="collapse list-unstyled sub-sub-menu" id="familiasSubmenu">
<li><a href="#"><span class="menu-item-text">Relações Familiares</span></a></li>
<li><a href="#"><span class="menu-item-text">Grupos Familiares</span></a></li>
<li><a href="#"><span class="menu-item-text">Composição Familiar</span></a></li>
</ul>
</li>
<!-- Cartões de Crédito -->
<li>
<a href="#cartoesSubmenu" data-bs-toggle="collapse" aria-expanded="false" class="dropdown-toggle">
<i data-lucide="credit-card"></i>
<span class="menu-item-text">Cartões de Crédito</span>
</a>
<ul class="collapse list-unstyled sub-sub-menu" id="cartoesSubmenu">
<li><a href="#"><span class="menu-item-text">Bandeiras</span></a></li>
<li><a href="#"><span class="menu-item-text">Cartões de Crédito</span></a></li>
</ul>
</li> </li>
</ul> </ul>
<div class="dropdown"> </li>
<a href="#" class="d-flex align-items-center text-dark text-decoration-none dropdown-toggle" id="dropdownUser1" data-bs-toggle="dropdown" aria-expanded="false">
<span class="me-2">Olá, <?php echo htmlspecialchars($user_name); ?></span> <!-- Despesas -->
</a> <li class="menu-section">
<ul class="dropdown-menu dropdown-menu-end text-small" aria-labelledby="dropdownUser1"> <a href="#despesasSubmenu" data-bs-toggle="collapse" aria-expanded="false" class="dropdown-toggle">
<li><a class="dropdown-item" href="profile.php">Meu Perfil</a></li> <i data-lucide="wallet"></i>
<li><hr class="dropdown-divider"></li> <span class="menu-item-text">Despesas</span>
<li><a class="dropdown-item" href="logout.php">Sair</a></li> </a>
</ul> <ul class="collapse list-unstyled sub-menu" id="despesasSubmenu">
</div> <li><a href="#"><span class="menu-item-text">Vencimento</span></a></li>
<?php endif; ?> <li>
<a href="#">
<span class="menu-item-text">Identificação de Despesas</span>
<?php if ($unclassifiedCount > 0): ?>
<span class="badge-notification"><?php echo $unclassifiedCount; ?></span>
<?php endif; ?>
</a>
</li>
<li><a href="expenses.php"><span class="menu-item-text">Movimentação Bancária</span></a></li>
<li><a href="#"><span class="menu-item-text">Despesas no Cartão</span></a></li>
<li><a href="#"><span class="menu-item-text">Previsão de Despesas</span></a></li>
</ul>
</li>
<!-- Análise de Dados -->
<li class="menu-section">
<a href="#analiseSubmenu" data-bs-toggle="collapse" aria-expanded="false" class="dropdown-toggle">
<i data-lucide="bar-chart-2"></i>
<span class="menu-item-text">Análise de Dados</span>
</a>
<ul class="collapse list-unstyled sub-menu" id="analiseSubmenu">
<li><a href="#"><span class="menu-item-text">Gráfico de Despesas por Áreas</span></a></li>
</ul>
</li>
</ul>
</nav>
<?php endif; ?>
<!-- Page Content -->
<div id="content" class="<?php echo !isset($_SESSION['user_id']) ? 'w-100' : '' ?>">
<?php if (isset($_SESSION['user_id'])): ?>
<div class="content-header">
<button type="button" id="sidebar-toggle" class="btn">
<i data-lucide="menu"></i>
</button>
</div> </div>
</div> <?php endif; ?>
</nav>
<?php if (isset($_SESSION['success_message'])): ?>
<div class="alert alert-success" role="alert">
<?php echo $_SESSION['success_message']; ?>
</div>
<?php unset($_SESSION['success_message']); ?>
<?php endif; ?>
<?php if (isset($_SESSION['error_message'])): ?>
<div class="alert alert-danger" role="alert">
<?php echo $_SESSION['error_message']; ?>
</div>
<?php unset($_SESSION['error_message']); ?>
<?php endif; ?>

37
includes/session.php Normal file
View File

@ -0,0 +1,37 @@
<?php
// Define um nome de sessão específico para a aplicação
$session_name = 'flatlogic_app_session';
session_name($session_name);
// Determina se a conexão é segura, considerando o proxy reverso (Cloudflare)
$is_secure = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ||
(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https');
// Configurações de segurança para o cookie da sessão
if ($is_secure) {
// Configuração para produção sob HTTPS (essencial para proxies)
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'domain' => '',
'secure' => true,
'httponly' => true,
'samesite' => 'None' // 'None' requer 'secure' => true
]);
} else {
// Configuração para desenvolvimento local (HTTP)
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'domain' => '',
'secure' => false,
'httponly' => true,
'samesite' => 'Lax'
]);
}
// Inicia a sessão
if (session_status() == PHP_SESSION_NONE) {
session_start();
}

176
index.php
View File

@ -1,172 +1,30 @@
<?php <?php
if (session_status() === PHP_SESSION_NONE) { require_once 'includes/session.php';
session_start();
}
// Verifica se o usuário está logado, redireciona para o login se não estiver
if (!isset($_SESSION['user_id'])) { if (!isset($_SESSION['user_id'])) {
header('Location: login.php'); header("Location: login.php");
exit; exit();
} }
require_once __DIR__ . '/db/config.php'; require_once 'db/config.php';
require_once 'includes/header.php';
$user_id = $_SESSION['user_id'];
$pdo = db();
$current_month_date = date('Y-m-01');
// --- Coleta de Dados para o Dashboard ---
// 1. Despesas do Mês Atual
$stmt_expenses = $pdo->prepare(
"SELECT category, SUM(amount) as total_spent FROM expenses
WHERE user_id = :user_id AND MONTH(expense_date) = MONTH(CURRENT_DATE()) AND YEAR(expense_date) = YEAR(CURRENT_DATE())
GROUP BY category"
);
$stmt_expenses->execute(['user_id' => $user_id]);
$monthly_expenses = $stmt_expenses->fetchAll(PDO::FETCH_KEY_PAIR);
// 2. Orçamentos do Mês Atual
$stmt_budgets = $pdo->prepare("SELECT category, amount FROM budgets WHERE user_id = :user_id AND budget_month = :budget_month");
$stmt_budgets->execute(['user_id' => $user_id, 'budget_month' => $current_month_date]);
$monthly_budgets = $stmt_budgets->fetchAll(PDO::FETCH_KEY_PAIR);
// 3. Consolidar dados de Orçamento e Despesas
$categories = ['Alimentação', 'Transporte', 'Moradia', 'Lazer', 'Saúde', 'Outros'];
$summary = [];
$total_spent_this_month = 0;
$total_budget_this_month = 0;
foreach ($categories as $category) {
$spent = $monthly_expenses[$category] ?? 0;
$budget = $monthly_budgets[$category] ?? 0;
$summary[$category] = [
'spent' => $spent,
'budget' => $budget,
'remaining' => $budget - $spent,
];
$total_spent_this_month += $spent;
$total_budget_this_month += $budget;
}
// 4. Últimas 5 despesas
$stmt_recent = $pdo->prepare("SELECT * FROM expenses WHERE user_id = :user_id ORDER BY expense_date DESC LIMIT 5");
$stmt_recent->execute(['user_id' => $user_id]);
$recent_expenses = $stmt_recent->fetchAll(PDO::FETCH_ASSOC);
// Função para determinar a classe da barra de progresso
function get_progress_bar_class($percentage) {
if ($percentage > 100) return 'bg-danger';
if ($percentage > 75) return 'bg-warning';
return 'bg-success';
}
include __DIR__ . '/includes/header.php';
?> ?>
<main class="container mt-4"> <div class="container-fluid">
<h1 class="mb-4">Dashboard de <?php echo date('F', strtotime('now')); ?></h1> <div class="row">
<div class="col-md-12">
<!-- Resumo Geral do Mês --> <div class="card">
<div class="card mb-4"> <div class="card-header">
<div class="card-body"> <h4 class="card-title">Dashboard</h4>
<div class="row align-items-center">
<div class="col-md-6">
<h4>Visão Geral do Mês</h4>
<p class="text-muted">Gastos vs. Orçamento Total</p>
<span class="fs-3 fw-bold">R$ <?php echo number_format($total_spent_this_month, 2, ',', '.'); ?></span>
<span class="fs-5 text-muted">/ R$ <?php echo number_format($total_budget_this_month, 2, ',', '.'); ?></span>
</div> </div>
<div class="col-md-6"> <div class="card-body">
<?php <p>Bem-vindo ao seu painel de controle de despesas!</p>
$overall_percentage = ($total_budget_this_month > 0) ? ($total_spent_this_month / $total_budget_this_month) * 100 : 0; <p>Use os links na barra de navegação para gerenciar suas despesas e orçamentos.</p>
$progress_class = get_progress_bar_class($overall_percentage);
?>
<div class="progress" style="height: 25px;">
<div class="progress-bar <?php echo $progress_class; ?>" role="progressbar"
style="width: <?php echo min(100, $overall_percentage); ?>%;"
aria-valuenow="<?php echo $overall_percentage; ?>" aria-valuemin="0" aria-valuemax="100">
<?php echo number_format($overall_percentage, 1); ?>%
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
<!-- Resumo por Categoria --> <?php require_once 'includes/footer.php'; ?>
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">Resumo do Orçamento por Categoria</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Categoria</th>
<th class="text-end">Gasto</th>
<th class="text-end">Orçamento</th>
<th class="text-end">Restante</th>
<th style="width: 25%;">Progresso</th>
</tr>
</thead>
<tbody>
<?php foreach ($summary as $category => $data): ?>
<tr>
<td><?php echo htmlspecialchars($category); ?></td>
<td class="text-end">R$ <?php echo number_format($data['spent'], 2, ',', '.'); ?></td>
<td class="text-end">R$ <?php echo number_format($data['budget'], 2, ',', '.'); ?></td>
<td class="text-end <?php echo ($data['remaining'] < 0) ? 'text-danger' : 'text-success'; ?>">
R$ <?php echo number_format($data['remaining'], 2, ',', '.'); ?>
</td>
<td>
<?php
$percentage = ($data['budget'] > 0) ? ($data['spent'] / $data['budget']) * 100 : 0;
$progress_class = get_progress_bar_class($percentage);
?>
<div class="progress">
<div class="progress-bar <?php echo $progress_class; ?>" role="progressbar"
style="width: <?php echo min(100, $percentage); ?>%;"
aria-valuenow="<?php echo $percentage; ?>" aria-valuemin="0" aria-valuemax="100">
</div>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<!-- Despesas Recentes -->
<div class="card">
<div class="card-header">
<h5 class="mb-0">Despesas Recentes</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table">
<tbody>
<?php if (empty($recent_expenses)): ?>
<tr><td class="text-center">Nenhuma despesa registrada este mês.</td></tr>
<?php else: ?>
<?php foreach ($recent_expenses as $expense): ?>
<tr>
<td><span class="badge bg-secondary me-2"><?php echo htmlspecialchars($expense['category']); ?></span> <?php echo htmlspecialchars($expense['description']); ?></td>
<td class="text-end">R$ <?php echo number_format($expense['amount'], 2, ',', '.'); ?></td>
<td class="text-end text-muted"><?php echo date('d/m/Y', strtotime($expense['expense_date'])); ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<div class="text-end mt-2">
<a href="expenses.php">Ver todas as despesas &rarr;</a>
</div>
</div>
</div>
</main>
<?php include __DIR__ . '/includes/footer.php'; ?>

View File

@ -1,9 +1,7 @@
<?php <?php
require_once __DIR__ . '/db/config.php'; require_once __DIR__ . '/db/config.php';
if (session_status() === PHP_SESSION_NONE) { require_once __DIR__ . '/includes/session.php';
session_start();
}
// Se já estiver logado, redireciona para o dashboard // Se já estiver logado, redireciona para o dashboard
if (isset($_SESSION['user_id'])) { if (isset($_SESSION['user_id'])) {
@ -27,20 +25,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$user = $stmt->fetch(PDO::FETCH_ASSOC); $user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user['password_hash'])) { if ($user && password_verify($password, $user['password_hash'])) {
// Login bem-sucedido // Login bem-sucedido: Redireciona com flag de depuração
$_SESSION['user_id'] = $user['id']; // Senha correta, inicie a sessão
header('Location: index.php'); $_SESSION['user_id'] = $user['id'];
exit; $_SESSION['user_email'] = $user['email'];
} else { header("Location: index.php");
// Credenciais inválidas exit();
$error_message = 'E-mail ou senha inválidos.';
}
if ($user && password_verify($password, $user['password_hash'])) {
// Login bem-sucedido
$_SESSION['user_id'] = $user['id'];
header('Location: index.php');
exit;
} else { } else {
// Credenciais inválidas // Credenciais inválidas
$error_message = 'E-mail ou senha inválidos.'; $error_message = 'E-mail ou senha inválidos.';