backend-01

commit b3c6de93dbd16b58e127cfc1734d58580b425db7

Author: Pedro Lucas Porcellis <porcellis@eletrotupi.com>

bills: add new bills with filtering page

 app/controllers/BillsController.php | 13 +++++
 app/daos/BillDAO.php | 56 ++++++++++++++++++++++++
 app/views/bills.php | 72 ++++++++++++++++++++++++++++++
 app/views/layout.php | 1 
 config/routes.php | 6 ++


diff --git a/app/controllers/BillsController.php b/app/controllers/BillsController.php
index 6a1d1a27cb81055d031f64ac8ac9f94285850f3b..fbafefe31c24055153b5629fbaa7b9fd836fd727 100644
--- a/app/controllers/BillsController.php
+++ b/app/controllers/BillsController.php
@@ -13,6 +13,19 @@     $this->billDAO = new BillDAO();
     $this->tagDAO = new TagDAO();
   }
 
+  public function index() {
+    $tags = $this->tagDAO->getAllTagsFromUser($_SESSION['user_id']);
+
+    $bills = $this->billDAO->getBillsByTagIdWithinRange(
+      $_SESSION['user_id'],
+      $_GET['tag_id'] ?? null ,
+      $_GET['start_date'] ?? null,
+      $_GET['end_date'] ?? null
+    );
+
+    return Template::render('bills', ['bills' => $bills, 'tags' => $tags]);
+  }
+
   public function create() {
     $tags = $this->tagDAO->getAllTagsFromUser($_SESSION['user_id']);
 




diff --git a/app/daos/BillDAO.php b/app/daos/BillDAO.php
index 14480ac3137645249a28a284bbef4af72126bd08..5b57045bfeed168afda2718765641d2d6c1b5514 100644
--- a/app/daos/BillDAO.php
+++ b/app/daos/BillDAO.php
@@ -39,6 +39,62 @@
     return $bills;
   }
 
+  public function getBillsByTagIdWithinRange($userId, $tagId, $startDate, $endDate) {
+    $sql = 'SELECT b.*, GROUP_CONCAT(t.name, ", ") AS tags
+            FROM bills b
+            LEFT JOIN bill_tags bt ON b.id = bt.bill_id
+            LEFT JOIN tags t ON bt.tag_id = t.id
+            WHERE b.user_id = :user_id';
+
+    if ($tagId) {
+      $sql .= ' AND b.id IN (
+        SELECT bill_id FROM bill_tags WHERE tag_id = :tag_id
+      )';
+    }
+
+    if ($startDate) {
+      $sql .= ' AND b.due_date >= :start_date';
+    }
+
+    if ($endDate) {
+      $sql .= ' AND b.due_date <= :end_date';
+    }
+
+    $sql .= ' GROUP BY b.id ORDER BY b.due_date DESC';
+
+    $stmt = $this->db->prepare($sql);
+    if ($tagId) {
+      $stmt->bindValue(':tag_id', $tagId);
+    }
+
+    if ($startDate) {
+      $stmt->bindValue(':start_date', $startDate);
+    }
+
+    if ($endDate) {
+      $stmt->bindValue(':end_date', $endDate);
+    }
+
+    $stmt->bindValue(':user_id', $userId);
+    $stmt->execute();
+
+    $bills = [];
+
+    while ($billData = $stmt->fetch(PDO::FETCH_ASSOC)) {
+      $bills[] = new Bill(
+        $billData['id'],
+        $billData['title'],
+        $billData['amount'],
+        $billData['due_date'],
+        $billData['paid'],
+        $billData['user_id'],
+        $billData['tags']
+      );
+    }
+
+    return $bills;
+  }
+
   public function create(Bill $bill, $tagIds) {
     $this->db->beginTransaction();
 




diff --git a/app/views/bills.php b/app/views/bills.php
new file mode 100644
index 0000000000000000000000000000000000000000..4dab882dbddd4ffd619bead59e5f13680d081c47
--- /dev/null
+++ b/app/views/bills.php
@@ -0,0 +1,72 @@
+<div class="container mx-auto mt-10">
+  <div class="bg-white shadow-md rounded-lg p-6 mb-6">
+    <form method="GET" action="/bills" class="flex flex-wrap gap-4">
+      <div class="flex-1">
+        <label for="start_date" class="block text-gray-700 text-sm font-bold mb-2">Data Inicial</label>
+        <input type="date" name="start_date" id="start_date" value="<?= htmlspecialchars($_GET['start_date'] ?? '') ?>"
+          class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600">
+      </div>
+      <div class="flex-1">
+        <label for="end_date" class="block text-gray-700 text-sm font-bold mb-2">Data Final</label>
+        <input type="date" name="end_date" id="end_date" value="<?= htmlspecialchars($_GET['end_date'] ?? '') ?>"
+          class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600">
+      </div>
+
+      <div class="flex-1">
+        <label for="tag_id" class="block text-gray-700 text-sm font-bold mb-2">Tags</label>
+        <select name="tag_id" id="tag" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-600">
+          <option value="">Todas as tags</option>
+          <?php foreach ($tags as $tag) : ?>
+            <option value="<?= $tag->getId() ?>" <?= (isset($_GET['tag_id']) && $_GET['tag_id'] == $tag->getId()) ? 'selected' : '' ?>>
+              <?= htmlspecialchars($tag->getName()) ?>
+            </option>
+          <?php endforeach; ?>
+        </select>
+      </div>
+      <div class="flex items-end">
+        <button type="submit" class="bg-blue-600 text-white py-2 px-4 rounded hover:bg-blue-700">
+          Filtrar
+        </button>
+      </div>
+    </form>
+  </div>
+
+  <?php if (empty($bills)) : ?>
+    <div class="text-center py-10 bg-white rounded-lg shadow-md">
+      <p class="text-xl text-gray-600">Nenhum gasto encontrado.</p>
+      <p class="text-gray-500 mt-2">Quem sabe tenta ajustar os filtros?</p>
+    </div>
+  <?php else : ?>
+    <div class="bg-white shadow-md rounded-lg overflow-hidden">
+      <table class="min-w-full table-auto">
+        <thead>
+          <tr class="bg-gray-200">
+            <th class="py-3 px-6 text-left">Título</th>
+            <th class="py-3 px-6 text-left">Valor</th>
+            <th class="py-3 px-6 text-left">Vencimento</th>
+            <th class="py-3 px-6 text-left">Tags</th>
+            <th class="py-3 px-6 text-left">Pago?</th>
+            <th class="py-3 px-6 text-left">Ações</th>
+          </tr>
+        </thead>
+        <tbody>
+          <?php foreach ($bills as $bill) : ?>
+            <tr class="border-b">
+              <td class="py-3 px-6"><?= htmlspecialchars($bill->getTitle()) ?></td>
+              <td class="py-3 px-6">R$ <?= number_format($bill->getAmount(), 2, ',', '.') ?></td>
+              <td class="py-3 px-6"><?= date('d/m/Y', strtotime($bill->getDueDate())) ?></td>
+              <td class="py-3 px-6"><?= htmlspecialchars($bill->getTags()) ?></td>
+              <td class="py-3 px-6"><?= $bill->isPaid() ? 'Sim' : 'Não' ?></td>
+              <td class="py-3 px-6 flex space-x-2">
+                <a href="/bills/edit/<?= $bill->getId() ?>" class="text-yellow-600 hover:text-yellow-700">Editar</a>
+                <a href="/bills/delete/<?= $bill->getId() ?>" class="text-red-600 hover:text-red-700">Apagar</a>
+              </td>
+            </tr>
+          <?php endforeach; ?>
+        </tbody>
+      </table>
+    </div>
+  <?php endif; ?>
+</div>
+</div>
+




diff --git a/app/views/layout.php b/app/views/layout.php
index 8a0b476d8b41a0670a50dc6beefa158ebe806eee..6db2a2ea2eeaef7a6fec31ab366b7a9109ca8f27 100644
--- a/app/views/layout.php
+++ b/app/views/layout.php
@@ -18,6 +18,7 @@
     <? if (isset($_SESSION['user_id'])): ?>
       <nav class="max-w-4xl mx-auto px-4 py-2">
         <a href="/tags" class="text-blue-700 mr-5">Tags</a>
+        <a href="/bills" class="text-blue-700 mr-5">Gastos</a>
         <a href="/logout" class="text-blue-700">Sair</a>
       </nav>
     <? endif; ?>




diff --git a/config/routes.php b/config/routes.php
index dfd8f161435c39ae55557cedc818fe1b59fae0c7..03883b1ff0f723d5c92fbea1978fb7a07e2d2bba 100644
--- a/config/routes.php
+++ b/config/routes.php
@@ -8,7 +8,7 @@ require_once '../app/controllers/BillsController.php';
 require_once '../app/controllers/TagsController.php';
 
 return function() {
-  $uri = trim($_SERVER['REQUEST_URI'], '/');
+  $uri = explode('?', trim($_SERVER['REQUEST_URI'], '/'))[0];
   $method = $_SERVER['REQUEST_METHOD'];
 
   // TODO: Meio burro isso
@@ -34,6 +34,10 @@
   // GET /dashboard
   } elseif ($uri === 'dashboard') {
     return (new DashboardController())->index();
+
+  // GET /bills
+  } elseif ($uri === 'bills' && $method === 'GET') {
+    return (new BillsController())->index();
 
   // GET /bills/create
   } elseif ($uri === 'bills/create' && $method === 'GET') {