duzo zmian i funkcji

This commit is contained in:
Mateusz Gruszczyński
2025-07-03 11:25:47 +02:00
parent b02bd27aa1
commit 79301398dd
14 changed files with 392 additions and 73 deletions

View File

@ -18,6 +18,7 @@
<div class="d-flex flex-wrap gap-2 mb-4">
<a href="/admin/users" class="btn btn-outline-light">👥 Lista użytkowników</a>
<a href="/admin/add_user" class="btn btn-success"> Dodaj użytkownika</a>
<a href="/admin/receipts" class="btn btn-outline-light">📸 Wszystkie paragony</a>
<a href="/admin/delete_all_lists" class="btn btn-danger">🗑️ Usuń wszystkie listy</a>
</div>

View File

@ -0,0 +1,39 @@
{% extends 'base.html' %}
{% block title %}Wszystkie paragony{% endblock %}
{% block content %}
<div class="d-flex justify-content-between align-items-center flex-wrap mb-4">
<h2 class="mb-2">📸 Wszystkie paragony</h2>
<a href="/admin" class="btn btn-outline-secondary">← Powrót do panelu</a>
</div>
<div class="row g-3">
{% for img in image_files %}
{% set list_id = img.split('_')[1] if '_' in img else None %}
{% set file_path = (upload_folder ~ '/' ~ img) %}
{% set file_size = (file_path | filesizeformat) %}
{% set upload_time = (file_path | filemtime) %}
<div class="col-6 col-md-4 col-lg-3">
<div class="card bg-dark text-white h-100">
<a href="{{ url_for('uploaded_file', filename=img) }}" data-lightbox="receipts" data-title="{{ img }}">
<img src="{{ url_for('uploaded_file', filename=img) }}" class="card-img-top" style="object-fit: cover; height: 200px;">
</a>
<div class="card-body text-center">
<p class="small text-truncate mb-1">{{ img }}</p>
<p class="small mb-1">Rozmiar: {{ file_size }}</p>
<p class="small mb-1">Wgrano: {{ upload_time.strftime('%Y-%m-%d %H:%M') }}</p>
{% if list_id %}
<a href="{{ url_for('view_list', list_id=list_id|int) }}" class="btn btn-sm btn-outline-light w-100 mb-2">🔗 Lista #{{ list_id }}</a>
{% endif %}
<a href="{{ url_for('delete_receipt', filename=img) }}" class="btn btn-sm btn-outline-danger w-100">🗑️ Usuń</a>
</div>
</div>
</div>
{% endfor %}
</div>
{% if not image_files %}
<p class="text-muted mt-3">Brak wgranych zdjęć.</p>
{% endif %}
{% endblock %}

View File

@ -5,6 +5,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}Live Lista Zakupów{% endblock %}</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.4/css/lightbox.min.css" rel="stylesheet">
<script src="https://cdn.socket.io/4.6.1/socket.io.min.js"></script>
<script src="{{ url_for('static_bp.serve_live_js') }}?v={{ time.time() | int }}"></script>
<script src="{{ url_for('static', filename='js/toasts.js') }}"></script>
@ -45,7 +47,9 @@
{% endwith %}
});
</script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.4/js/lightbox.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -26,24 +26,47 @@
<ul id="items" class="list-group mb-3">
{% for item in items %}
<li class="list-group-item d-flex justify-content-between align-items-center flex-wrap {% if item.purchased %}bg-success text-white{% else %}bg-light text-white{% endif %}" id="item-{{ item.id }}">
<div class="d-flex align-items-center flex-wrap gap-2">
<input type="checkbox" {% if item.purchased %}checked{% endif %}>
<span id="name-{{ item.id }}" class="{% if item.purchased %}text-white{% else %}text-white{% endif %}">{{ item.name }}</span>
</div>
<div class="mt-2 mt-md-0">
<button class="btn btn-sm btn-outline-warning me-1" onclick="editItem({{ item.id }}, '{{ item.name }}')">✏️ Edytuj</button>
<button class="btn btn-sm btn-outline-danger" onclick="deleteItem({{ item.id }})">🗑️ Usuń</button>
</div>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center flex-wrap {% if item.purchased %}bg-success text-white{% else %}bg-light text-white{% endif %}" id="item-{{ item.id }}">
<div class="d-flex align-items-center flex-wrap gap-2 flex-grow-1">
<input type="checkbox" {% if item.purchased %}checked{% endif %}>
<span id="name-{{ item.id }}" class="{% if item.purchased %}text-white{% else %}text-white{% endif %}">{{ item.name }}</span>
{% if item.note %}
<small class="text-danger">[ Notatka: <b>{{ item.note }}</b> ] </small>
{% endif %}
</div>
<div class="mt-2 mt-md-0 d-flex gap-1">
<button class="btn btn-sm btn-outline-warning" onclick="editItem({{ item.id }}, '{{ item.name }}')">✏️ Edytuj</button>
<button class="btn btn-sm btn-outline-danger" onclick="deleteItem({{ item.id }})">🗑️ Usuń</button>
</div>
</li>
{% endfor %}
</ul>
<div class="input-group mb-2 position-relative">
<input id="newItem" class="form-control" placeholder="Nowy produkt">
<button onclick="addItem({{ list.id }})" class="btn btn-success"> Dodaj</button>
</div>
{% set receipt_pattern = 'list_' ~ list.id %}
{% if receipt_files %}
<hr>
<h5 class="mt-4">📸 Paragony dodane do tej listy</h5>
<div class="row g-3 mt-2">
{% for file in receipt_files %}
<div class="col-6 col-md-4 col-lg-3 text-center">
<a href="{{ url_for('uploaded_file', filename=file) }}" data-lightbox="receipt" data-title="Paragon">
<img src="{{ url_for('uploaded_file', filename=file) }}" class="img-fluid rounded shadow-sm border border-secondary" style="max-height: 200px; object-fit: cover;">
</a>
</div>
{% endfor %}
</div>
{% else %}
<hr>
<p class="text-muted">Brak wgranych paragonów do tej listy.</p>
{% endif %}
<div id="toast-container" class="toast-container position-fixed bottom-0 end-0 p-3"></div>
<script>

View File

@ -9,12 +9,16 @@
<ul id="items" class="list-group mb-3">
{% for item in items %}
<li class="list-group-item d-flex justify-content-between align-items-center flex-wrap clickable-item {% if item.purchased %}bg-success text-white{% else %}bg-light text-white{% endif %}" id="item-{{ item.id }}">
<div class="d-flex align-items-center gap-3 flex-grow-1">
<input type="checkbox" class="form-check-input large-checkbox" {% if item.purchased %}checked{% endif %}>
<span id="name-{{ item.id }}" class="{% if item.purchased %}text-white{% else %}text-white{% endif %}">{{ item.name }}</span>
</div>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center flex-wrap clickable-item {% if item.purchased %}bg-success text-white{% else %}bg-light text-white{% endif %}" id="item-{{ item.id }}">
<div class="d-flex align-items-center gap-3 flex-grow-1">
<input type="checkbox" class="form-check-input large-checkbox" {% if item.purchased %}checked{% endif %}>
<span id="name-{{ item.id }}" class="{% if item.purchased %}text-white{% else %}text-white{% endif %}">{{ item.name }}</span>
{% if item.note %}
<small class="text-danger ms-4">[ Notatka: <b>{{ item.note }}</b> ]</small>
{% endif %}
</div>
<button type="button" class="btn btn-sm btn-outline-info" onclick="openNoteModal(event, {{ item.id }})">📝 Notatka</button>
</li>
{% endfor %}
</ul>
@ -25,40 +29,61 @@
<div id="toast-container" class="toast-container position-fixed bottom-0 end-0 p-3"></div>
{% set receipt_pattern = 'list_' ~ list.id %}
{% if receipt_files %}
<hr>
<h5 class="mt-4">📸 Paragony dodane do tej listy</h5>
<div class="row g-3 mt-2">
{% for file in receipt_files %}
<div class="col-6 col-md-4 col-lg-3 text-center">
<a href="{{ url_for('uploaded_file', filename=file) }}" data-lightbox="receipt" data-title="Paragon">
<img src="{{ url_for('uploaded_file', filename=file) }}" class="img-fluid rounded shadow-sm border border-secondary" style="max-height: 200px; object-fit: cover;">
</a>
</div>
{% endfor %}
</div>
{% else %}
<hr>
<p class="text-muted">Brak wgranych paragonów do tej listy.</p>
{% endif %}
<hr>
<h5>📤 Dodaj zdjęcie paragonu</h5>
<form action="{{ url_for('upload_receipt', list_id=list.id) }}" method="post" enctype="multipart/form-data">
<div class="input-group mb-2">
<input type="file" name="receipt" accept="image/*" capture="environment" class="form-control" id="receiptInput">
<button type="submit" class="btn btn-success"> Wgraj</button>
</div>
</form>
<!-- Modal notatki -->
<div class="modal fade" id="noteModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content bg-dark text-white">
<div class="modal-header">
<h5 class="modal-title">Dodaj notatkę</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Zamknij"></button>
</div>
<form id="noteForm" onsubmit="submitNote(event)">
<div class="modal-body">
<textarea id="noteText" class="form-control" rows="4" placeholder="Np. 'Nie było, zamieniłem na inny'"></textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Anuluj</button>
<button type="submit" class="btn btn-success">💾 Zapisz</button>
</div>
</form>
</div>
</div>
</div>
<script>
setupList({{ list.id }}, 'Gość');
document.querySelectorAll('.clickable-item').forEach(item => {
item.addEventListener('click', function(e) {
if (e.target.tagName.toLowerCase() !== 'input') {
const checkbox = this.querySelector('input[type="checkbox"]');
if (checkbox.disabled) {
return;
}
if (checkbox.checked) {
socket.emit('uncheck_item', { item_id: parseInt(this.id.replace('item-', ''), 10) });
} else {
socket.emit('check_item', { item_id: parseInt(this.id.replace('item-', ''), 10) });
}
checkbox.disabled = true;
this.classList.add('opacity-50');
let existingSpinner = this.querySelector('.spinner-border');
if (!existingSpinner) {
const spinner = document.createElement('span');
spinner.className = 'spinner-border spinner-border-sm ms-2';
spinner.setAttribute('role', 'status');
spinner.setAttribute('aria-hidden', 'true');
checkbox.parentElement.appendChild(spinner);
}
}
});
});
const LIST_ID = {{ list.id }};
setupList(LIST_ID, 'Gość');
</script>
<script src="{{ url_for('static', filename='js/list_guest.js') }}"></script>
<style>
.large-checkbox {
width: 1.5em;

View File

@ -4,7 +4,6 @@
<div class="d-flex justify-content-between align-items-center flex-wrap mb-4">
<h2 class="mb-2">🔑 Hasło do systemu</h2>
<a href="{{ url_for('index_guest') }}" class="btn btn-outline-secondary">← Powrót do list</a>
</div>
<div class="card bg-dark text-white">