FinanceTracker/src/main/resources/templates/transactions.html

224 lines
10 KiB
HTML

<!DOCTYPE html>
<html data-bs-theme="light" lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
<title>FinanceTracker | Transactions</title>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.min.css">
<!-- Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<style>
/* Fixed table layout and numeric alignment */
#dataTable {
table-layout: fixed;
width: 100%;
}
.numeric {
white-space: nowrap;
text-align: right;
}
/* Truncate description with ellipsis */
.description {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Row color coding */
.txn-positive td {
background-color: rgba(0, 255, 0, 0.15);
}
.txn-negative td {
background-color: rgba(255, 0, 0, 0.15);
}
body {
background-color: #f8f9fc;
}
.card {
border: none;
border-radius: 0.75rem;
}
.card-header {
background: transparent;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}
.material-icons {
vertical-align: middle;
font-size: 20px;
}
.scroll-to-top {
position: fixed;
right: 1rem;
bottom: 1rem;
width: 40px;
height: 40px;
background-color: #0d6efd;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
}
</style>
<body id="page-top">
<div id="wrapper">
<!--Sidebar - Navigation-->
<div th:replace="~{fragments/sidebar-nav :: sidebar-nav}"></div>
<div class="d-flex flex-column" id="content-wrapper">
<div id="content">
<!-- Topbar -->
<div th:replace="~{fragments/topbar :: topbar}"></div>
<div class="container-fluid">
<div class="card shadow">
<div class="d-inline-flex align-items-center card-header py-3">
<p class="text-primary m-0 fw-bold w-100">Transactions</p>
<a th:href="@{/addTransaction}" class="d-inline-flex btn btn-primary">
<i class="bi bi-plus"></i>Add
</a>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6 text-nowrap">
<div id="dataTable_length" class="dataTables_length" aria-controls="dataTable">
<label class="form-label">Show&nbsp;
<select th:hx-get="@{/changePageSize}" hx-target="#dataTable"
hx-trigger="input changed" hx-swap="outerHtml" name="pageSizeCB"
class="d-inline-block form-select form-select-sm">
<option value="10" selected="">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>&nbsp;
</label>
</div>
</div>
<div class="col-md-6">
<div class="text-md-end dataTables_filter" id="dataTable_filter">
<label class="form-label">
<input hx-trigger="input changed delay:1s" hx-swap="outerHtml"
hx-target="#dataTable" hx-get="/searchTransactions" type="search"
class="form-control form-control-sm" aria-controls="dataTable"
name="searchValue" placeholder="Search">
</label>
</div>
</div>
</div>
<div class="table-responsive" style="overflow-x: auto;">
<table th:fragment="transactionTable" class="table my-0" id="dataTable">
<!-- Fixed column widths -->
<colgroup>
<col style="width: 130px;"> <!-- Money In -->
<col style="width: 130px;"> <!-- Money Out -->
<col style="width: 110px;"> <!-- Fees -->
<col style="width: 140px;"> <!-- Balance -->
<col style="width: 120px;"> <!-- Date -->
<col style="width: 160px;"> <!-- Category -->
<col style="width: 260px;"> <!-- Description -->
</colgroup>
<thead>
<tr>
<th class="numeric">Money In</th>
<th class="numeric">Money Out</th>
<th class="numeric">Fees</th>
<th class="numeric">Balance</th>
<th>Date</th>
<th>Category</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr th:each="transaction : ${transactions}"
th:with="rowClass=${(transaction.moneyIn + transaction.moneyOut + transaction.fee) < 0 ? 'txn-negative' : 'txn-positive'}"
th:class="${rowClass}">
<td class="numeric"
th:text="${#numbers.formatCurrency(transaction.moneyIn)}"></td>
<td class="numeric"
th:text="${#numbers.formatCurrency(transaction.moneyOut)}"></td>
<td class="numeric" th:text="${#numbers.formatCurrency(transaction.fee)}">
</td>
<td class="numeric"
th:text="${#numbers.formatCurrency(transaction.balance)}"></td>
<td th:text="${transaction.date}"></td>
<td th:text="${transaction.category}"></td>
<td class="description" th:text="${transaction.description}"></td>
</tr>
</tbody>
</table>
</div>
<div th:fragment="tableInfo" class="row">
<div class="col-md-6 align-self-center">
<p id="dataTable_info" class="dataTables_info" role="status" aria-live="polite"
th:text="'Showing ' + ${pageNum + 1} + ' to ' + ${pageSize} + ' of ' + ${transactionCount}">
Showing 1 to 10 of 27</p>
</div>
<div class="col-md-6">
<nav
class="d-lg-flex justify-content-lg-end dataTables_paginate paging_simple_numbers">
<ul class="pagination">
<li th:classappend="${(pageNum + 1 == 1) ? 'disabled' : ''}"
class="page-item">
<a th:hx-get="@{/previousPage}" hx-trigger="click"
hx-target="#dataTable" hx-swap="outerHtml" class="page-link btn"
aria-label="Previous">
<span aria-hidden="true">« Previous</span>
</a>
</li>
<li th:classappend="${(pageNum == pageSize - 1) ? 'disabled' : ''}"
class="page-item">
<a th:hx-get="@{/nextPage}" hx-trigger="click" hx-target="#dataTable"
hx-swap="outerHtml" class="page-link btn" aria-label="Next">
<span aria-hidden="true">Next »</span></a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
<!--Footer-->
<div th:replace="~{fragments/footer :: footer}"></div>
</div>
<a class="border rounded d-inline scroll-to-top" href="#page-top">
<span class="material-icons">keyboard_arrow_up</span>
</a>
</div>
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js"></script>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="assets/js/bs-init.js"></script>
<script src="assets/js/theme.js"></script>
</body>
</html>