diff --git a/app.py b/app.py index c5af0e9..7883cc6 100644 --- a/app.py +++ b/app.py @@ -49,14 +49,19 @@ def load_user(user_id): @app.before_request def require_system_password(): - if 'authorized' not in request.cookies and request.endpoint != 'system_auth' and not request.endpoint.startswith('static') and not request.endpoint.startswith('login'): - return redirect(url_for('system_auth')) + if 'authorized' not in request.cookies \ + and request.endpoint != 'system_auth' \ + and not request.endpoint.startswith('static') \ + and not request.endpoint.startswith('login'): + return redirect(url_for('system_auth', next=request.url)) @app.route('/system-auth', methods=['GET', 'POST']) def system_auth(): DEFAULT_ADMIN_USERNAME = app.config.get('DEFAULT_ADMIN_USERNAME', 'admin') DEFAULT_ADMIN_PASSWORD = app.config.get('DEFAULT_ADMIN_PASSWORD', 'admin123') + next_page = request.args.get('next') or url_for('index_guest') + if request.method == 'POST': if request.form['password'] == SYSTEM_PASSWORD: db.create_all() @@ -69,7 +74,7 @@ def system_auth(): db.session.add(admin_user) db.session.commit() flash(f'Utworzono konto administratora: login={DEFAULT_ADMIN_USERNAME}, hasło={DEFAULT_ADMIN_PASSWORD}') - resp = redirect(url_for('index_guest')) + resp = redirect(next_page) resp.set_cookie('authorized', 'true') return resp flash('Nieprawidłowe hasło do systemu') @@ -80,6 +85,10 @@ def index_guest(): lists = ShoppingList.query.all() return render_template('index.html', lists=lists) +@app.errorhandler(404) +def page_not_found(e): + return render_template('404.html'), 404 + @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': @@ -128,6 +137,12 @@ def share_list(token): items = Item.query.filter_by(list_id=shopping_list.id).all() return render_template('list_guest.html', list=shopping_list, items=items) +@app.route('/guest-list/') +def guest_list(list_id): + shopping_list = ShoppingList.query.get_or_404(list_id) + items = Item.query.filter_by(list_id=list_id).all() + return render_template('list_guest.html', list=shopping_list, items=items) + @app.route('/copy/') @login_required def copy_list(list_id): @@ -281,6 +296,16 @@ def handle_check_item(data): db.session.commit() emit('item_checked', {'item_id': item.id}, to=str(item.list_id)) +@socketio.on('uncheck_item') +def handle_uncheck_item(data): + item = Item.query.get(data['item_id']) + if item: + item.purchased = False + item.purchased_at = None + db.session.commit() + emit('item_unchecked', {'item_id': item.id}, to=str(item.list_id)) + + @app.cli.command('create_db') def create_db(): db.create_all() diff --git a/static/js/live.js b/static/js/live.js index 421b0e5..abd54bd 100644 --- a/static/js/live.js +++ b/static/js/live.js @@ -14,25 +14,41 @@ function setupList(listId, username) { }); } + const itemsContainer = document.getElementById('items'); + + // Delegacja zdarzenia checkboxów + itemsContainer.addEventListener('change', function (e) { + if (e.target && e.target.type === 'checkbox') { + const li = e.target.closest('li'); + if (li) { + const id = parseInt(li.id.replace('item-', ''), 10); + if (e.target.checked) { + socket.emit('check_item', { item_id: id }); + } else { + socket.emit('uncheck_item', { item_id: id }); + } + } + } + }); + socket.on('user_joined', data => showToast(`${data.username} dołączył do listy`)); socket.on('item_added', data => { showToast(`${data.added_by} dodał: ${data.name}`); - const list = document.getElementById('items'); const li = document.createElement('li'); - li.className = 'list-group-item bg-dark text-white d-flex justify-content-between align-items-center'; + li.className = 'list-group-item bg-dark text-white d-flex justify-content-between align-items-center flex-wrap'; li.id = `item-${data.id}`; li.innerHTML = ` -
- +
+ ${data.name}
-
- - +
+ +
`; - list.appendChild(li); + itemsContainer.appendChild(li); }); socket.on('item_checked', data => { @@ -42,6 +58,13 @@ function setupList(listId, username) { } }); + socket.on('item_unchecked', data => { + const checkbox = document.querySelector(`#item-${data.item_id} input[type='checkbox']`); + if (checkbox) { + checkbox.checked = false; + } + }); + socket.on('item_deleted', data => { const li = document.getElementById(`item-${data.item_id}`); if (li) { @@ -67,10 +90,6 @@ function addItem(listId) { document.getElementById('newItem').focus(); } -function checkItem(id) { - socket.emit('check_item', { item_id: id }); -} - function deleteItem(id) { if (confirm('Na pewno usunąć produkt?')) { socket.emit('delete_item', { item_id: id }); diff --git a/templates/404.html b/templates/404.html new file mode 100644 index 0000000..cb8ded7 --- /dev/null +++ b/templates/404.html @@ -0,0 +1,17 @@ +{% extends 'base.html' %} +{% block title %}Strona nie znaleziona{% endblock %} +{% block content %} + +
+

404 — Strona nie znaleziona

+ ← Powrót na stronę główną +
+ +
+
+

Ups! Podana strona nie istnieje lub została przeniesiona.

+

Sprawdź adres lub wróć na stronę główną, aby kontynuować.

+
+
+ +{% endblock %} diff --git a/templates/base.html b/templates/base.html index c00d0ae..9314f0c 100644 --- a/templates/base.html +++ b/templates/base.html @@ -2,6 +2,7 @@ + {% block title %}Live Lista Zakupów{% endblock %} @@ -9,20 +10,23 @@ -