feat: implement local payment QR codes and update AI co-authoring rules
All checks were successful
Deploy to K8s / deploy (push) Successful in 11s
Build and Push / build (push) Successful in 8s

QR codes are now generated locally using the 'qrcode' library for better privacy and reliability.
Updated .agent/rules.md with co-author details and Conventional Commits preference.

Co-authored-by: Antigravity <antigravity@google.com>
This commit is contained in:
Jan Novak
2026-03-02 22:54:48 +01:00
parent b0276f68b3
commit 4bb8c7420c
3 changed files with 188 additions and 19 deletions

View File

@@ -138,6 +138,28 @@
.cell-unpaid {
color: #ff3333;
background-color: rgba(255, 51, 51, 0.05);
position: relative;
}
.pay-btn {
display: none;
position: absolute;
right: 5px;
top: 50%;
transform: translateY(-50%);
background: #ff3333;
color: white;
border: none;
border-radius: 3px;
padding: 2px 6px;
font-size: 10px;
cursor: pointer;
font-weight: bold;
}
.member-row:hover .pay-btn {
display: inline-block;
}
.cell-empty {
@@ -360,6 +382,42 @@
margin-top: 5px;
color: #222;
}
/* QR Modal styles */
#qrModal .modal-content {
max-width: 400px;
text-align: center;
}
.qr-image {
background: white;
padding: 10px;
border-radius: 5px;
margin: 20px 0;
display: inline-block;
}
.qr-image img {
display: block;
width: 250px;
height: 250px;
}
.qr-details {
text-align: left;
margin-top: 15px;
font-size: 14px;
color: #ccc;
}
.qr-details div {
margin-bottom: 5px;
}
.qr-details span {
color: #00ff00;
font-family: monospace;
}
</style>
</head>
@@ -403,8 +461,12 @@
</td>
{% for cell in row.months %}
<td
class="{% if cell == '-' %}cell-empty{% elif 'UNPAID' in cell %}cell-unpaid{% elif cell == 'OK' %}cell-ok{% endif %}">
{{ cell }}
class="{% if cell.status == 'empty' %}cell-empty{% elif cell.status == 'unpaid' or cell.status == 'partial' %}cell-unpaid{% elif cell.status == 'ok' %}cell-ok{% endif %}">
{{ cell.text }}
{% if cell.status == 'unpaid' or cell.status == 'partial' %}
<button class="pay-btn"
onclick="showPayQR('{{ row.name|e }}', {{ cell.amount }}, '{{ cell.month|e }}')">Pay</button>
{% endif %}
</td>
{% endfor %}
<td class="{% if row.balance > 0 %}balance-pos{% elif row.balance < 0 %}balance-neg{% endif %}">
@@ -460,6 +522,25 @@
</div>
{% endif %}
<!-- QR Code Modal -->
<div id="qrModal" class="modal"
style="display:none; position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background-color: rgba(0, 0, 0, 0.9); z-index: 9999; justify-content: center; align-items: center;">
<div class="modal-content">
<div class="modal-header">
<div class="modal-title" id="qrTitle">Payment for ...</div>
<div class="close-btn" onclick="closeModal('qrModal')">[close]</div>
</div>
<div class="qr-image">
<img id="qrImg" src="" alt="Payment QR Code">
</div>
<div class="qr-details">
<div>Account: <span id="qrAccount"></span></div>
<div>Amount: <span id="qrAmount"></span> CZK</div>
<div>Message: <span id="qrMessage"></span></div>
</div>
</div>
</div>
<div id="memberModal">
<div class="modal-content">
<div class="modal-header">
@@ -620,8 +701,15 @@
document.getElementById('memberModal').classList.add('active');
}
function closeModal() {
document.getElementById('memberModal').classList.remove('active');
function closeModal(id) {
if (id) {
document.getElementById(id).style.display = 'none';
if (id === 'qrModal') {
document.getElementById(id).style.display = 'none';
}
} else {
document.getElementById('memberModal').classList.remove('active');
}
}
// Existing filter script
@@ -644,6 +732,33 @@
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') closeModal();
});
function showPayQR(name, amount, month) {
const account = "{{ bank_account }}";
const message = `${name} / ${month}`;
const qrTitle = document.getElementById('qrTitle');
const qrImg = document.getElementById('qrImg');
const qrAccount = document.getElementById('qrAccount');
const qrAmount = document.getElementById('qrAmount');
const qrMessage = document.getElementById('qrMessage');
qrTitle.innerText = `Payment for ${month}`;
qrAccount.innerText = account;
qrAmount.innerText = amount;
qrMessage.innerText = message;
const encodedMessage = encodeURIComponent(message);
const qrUrl = `/qr?account=${encodeURIComponent(account)}&amount=${amount}&message=${encodedMessage}`;
qrImg.src = qrUrl;
document.getElementById('qrModal').style.display = 'block';
}
// Close modal when clicking outside
window.onclick = function (event) {
if (event.target.className === 'modal') {
event.target.style.display = 'none';
}
}
</script>
</body>