versao 4
This commit is contained in:
parent
a0c63edc92
commit
07e06596db
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -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>
|
||||||
@ -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">
|
|
||||||
<i class="bi bi-safe me-2"></i>
|
|
||||||
Galilei Finance
|
|
||||||
</a>
|
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
|
||||||
<span class="navbar-toggler-icon"></span>
|
|
||||||
</button>
|
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
|
||||||
<?php if (isset($_SESSION['user_id'])): ?>
|
<?php if (isset($_SESSION['user_id'])): ?>
|
||||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
<nav id="sidebar">
|
||||||
<li class="nav-item">
|
<div class="sidebar-header">
|
||||||
<a class="nav-link" href="index.php">Dashboard</a>
|
<h3>Concilia Fácil</h3>
|
||||||
</li>
|
</div>
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="expenses.php">Despesas</a>
|
<div class="profile-section dropdown">
|
||||||
</li>
|
<a href="#" class="dropdown-toggle" id="dropdownUser1" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<li class="nav-item">
|
<i data-lucide="user-circle"></i>
|
||||||
<a class="nav-link" href="budgets.php">Orçamentos</a>
|
<div class="profile-text">
|
||||||
</li>
|
<span class="username"><?php echo htmlspecialchars($first_name); ?></span>
|
||||||
</ul>
|
<span>Meu Perfil</span>
|
||||||
<div class="dropdown">
|
</div>
|
||||||
<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>
|
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu dropdown-menu-end text-small" aria-labelledby="dropdownUser1">
|
<ul class="dropdown-menu dropdown-menu-dark" aria-labelledby="dropdownUser1">
|
||||||
<li><a class="dropdown-item" href="profile.php">Meu Perfil</a></li>
|
<li><a class="dropdown-item" href="profile.php">Ver Perfil</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li><hr class="dropdown-divider"></li>
|
||||||
<li><a class="dropdown-item" href="logout.php">Sair</a></li>
|
<li><a class="dropdown-item" href="logout.php">Sair</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</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>
|
||||||
|
<!-- Bancos -->
|
||||||
|
<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>
|
||||||
|
<!-- Famílias -->
|
||||||
|
<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>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<!-- Despesas -->
|
||||||
|
<li class="menu-section">
|
||||||
|
<a href="#despesasSubmenu" data-bs-toggle="collapse" aria-expanded="false" class="dropdown-toggle">
|
||||||
|
<i data-lucide="wallet"></i>
|
||||||
|
<span class="menu-item-text">Despesas</span>
|
||||||
|
</a>
|
||||||
|
<ul class="collapse list-unstyled sub-menu" id="despesasSubmenu">
|
||||||
|
<li><a href="#"><span class="menu-item-text">Vencimento</span></a></li>
|
||||||
|
<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; ?>
|
<?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>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (isset($_SESSION['success_message'])): ?>
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
|
<?php echo $_SESSION['success_message']; ?>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
<?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
37
includes/session.php
Normal 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();
|
||||||
|
}
|
||||||
172
index.php
172
index.php
@ -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 mb-4">
|
|
||||||
<div class="card-body">
|
|
||||||
<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 class="col-md-6">
|
|
||||||
<?php
|
|
||||||
$overall_percentage = ($total_budget_this_month > 0) ? ($total_spent_this_month / $total_budget_this_month) * 100 : 0;
|
|
||||||
$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>
|
|
||||||
|
|
||||||
<!-- Resumo por Categoria -->
|
|
||||||
<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">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h5 class="mb-0">Despesas Recentes</h5>
|
<h4 class="card-title">Dashboard</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="table-responsive">
|
<p>Bem-vindo ao seu painel de controle de despesas!</p>
|
||||||
<table class="table">
|
<p>Use os links na barra de navegação para gerenciar suas despesas e orçamentos.</p>
|
||||||
<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 →</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<?php include __DIR__ . '/includes/footer.php'; ?>
|
<?php require_once 'includes/footer.php'; ?>
|
||||||
|
|||||||
22
login.php
22
login.php
@ -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
|
||||||
|
// Senha correta, inicie a sessão
|
||||||
$_SESSION['user_id'] = $user['id'];
|
$_SESSION['user_id'] = $user['id'];
|
||||||
header('Location: index.php');
|
$_SESSION['user_email'] = $user['email'];
|
||||||
exit;
|
header("Location: index.php");
|
||||||
} else {
|
exit();
|
||||||
// Credenciais inválidas
|
|
||||||
$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.';
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user