kolory kategorii i jedniklikowy wybor kategorii w modalu

This commit is contained in:
Mateusz Gruszczyński
2025-10-07 09:10:29 +02:00
parent 735fc69562
commit 81744b5c5e
3 changed files with 66 additions and 17 deletions

43
app.py
View File

@@ -885,12 +885,13 @@ def get_admin_expense_summary():
}
def category_to_color(name):
hash_val = int(hashlib.md5(name.encode("utf-8")).hexdigest(), 16)
hue = (hash_val % 360) / 360.0
saturation = 0.60 + ((hash_val >> 8) % 17) / 100.0
lightness = 0.28 + ((hash_val >> 16) % 11) / 100.0
import hashlib, colorsys
def category_to_color(name: str) -> str:
hash_val = int(hashlib.md5(name.encode("utf-8")).hexdigest(), 16)
hue = ((hash_val % 360) + ((hash_val >> 24) % 20)) % 360 / 360.0
saturation = 0.55 + ((hash_val >> 8) % 40) / 100.0
lightness = 0.35 + ((hash_val >> 16) % 30) / 100.0
r, g, b = colorsys.hls_to_rgb(hue, lightness, saturation)
return f"#{int(r*255):02x}{int(g*255):02x}{int(b*255):02x}"
@@ -2110,10 +2111,6 @@ def create_list():
@app.route("/list/<int:list_id>")
@login_required
# ─────────────────────────────────────────────────────────────────────────────
# Widok listy właściciela dopięcie permitted_users do kontekstu
# ─────────────────────────────────────────────────────────────────────────────
@login_required
def view_list(list_id):
shopping_list = db.session.get(ShoppingList, list_id)
if not shopping_list:
@@ -2126,30 +2123,45 @@ def view_list(list_id):
flash("W celu modyfikacji listy, przejdź do panelu administracyjnego.", "info")
return redirect(url_for("shared_list", token=shopping_list.share_token))
# Twoja obecna logika ładująca szczegóły listy:
shopping_list, items, receipts, expenses, total_expense = get_list_details(list_id)
total_count = len(items)
purchased_count = len([i for i in items if i.purchased])
percent = (purchased_count / total_count * 100) if total_count > 0 else 0
# Uzupełnienie "added_by_display" — jak u Ciebie:
for item in items:
if item.added_by != shopping_list.owner_id:
item.added_by_display = (item.added_by_user.username if item.added_by_user else "?")
else:
item.added_by_display = None
# Badges kategorii (jak u Ciebie)
shopping_list.category_badges = [
{"name": c.name, "color": category_to_color(c.name)}
for c in shopping_list.categories
]
# Dane do modala kategorii
# Wszystkie kategorie (do selecta)
categories = Category.query.order_by(Category.name.asc()).all()
selected_categories_ids = {c.id for c in shopping_list.categories}
# ⬅️ NOWE: użytkownicy z uprawnieniami do tej listy (dla modala w list.html)
# Najczęściej używane kategorie właściciela (top N)
popular_categories = (
db.session.query(Category)
.join(
shopping_list_category,
shopping_list_category.c.category_id == Category.id,
)
.join(
ShoppingList,
ShoppingList.id == shopping_list_category.c.shopping_list_id,
)
.filter(ShoppingList.owner_id == current_user.id)
.group_by(Category.id)
.order_by(func.count(ShoppingList.id).desc(), func.lower(Category.name).asc())
.limit(6)
.all()
)
# Użytkownicy z uprawnieniami do listy
permitted_users = (
db.session.query(User)
.join(ListPermission, ListPermission.user_id == User.id)
@@ -2172,7 +2184,8 @@ def view_list(list_id):
is_owner=is_owner,
categories=categories,
selected_categories=selected_categories_ids,
permitted_users=permitted_users, # ⬅️ ważne dla tokenów w modalu
permitted_users=permitted_users,
popular_categories=popular_categories,
)

View File

@@ -0,0 +1,18 @@
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('#categoriesModal .category-suggestion').forEach(btn => {
btn.addEventListener('click', () => {
const select = document.getElementById('category_id');
if (!select) return;
select.value = btn.dataset.catId || '';
const form = btn.closest('form');
if (form) {
if (typeof form.requestSubmit === 'function') {
form.requestSubmit();
} else {
form.submit();
}
}
});
});
});

View File

@@ -204,12 +204,29 @@
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content bg-dark text-white">
<div class="modal-header">
<h5 class="modal-title" id="grantAccessModalLabel">Ustaw kategorię</h5>
<h5 class="modal-title" id="categoriesModalLabel">Ustaw kategorię</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Zamknij"></button>
</div>
<form method="post" action="{{ url_for('list_settings', list_id=list.id) }}">
<div class="modal-body">
{% if popular_categories %}
<div class="mb-3">
<div class="small text-secondary mb-1">Najczęściej używane:</div>
<div class="d-flex flex-wrap gap-2">
{% for cat in popular_categories %}
<button type="button" class="btn btn-sm btn-outline-light category-suggestion" data-cat-id="{{ cat.id }}">
{{ cat.name }}
</button>
{% endfor %}
<button type="button" class="btn btn-sm btn-outline-secondary category-suggestion" data-cat-id="">
brak
</button>
</div>
</div>
{% endif %}
<div class="mb-4">
<label for="category_id" class="form-label">🏷️ Kategoria listy</label>
<select id="category_id" name="category_id"
@@ -234,11 +251,11 @@
</div>
</div>
</form>
</div>
</div>
</div>
<!-- MODAL: NADAWANIE DOSTĘPU -->
<div class="modal fade" id="grantAccessModal" tabindex="-1" aria-labelledby="grantAccessModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
@@ -320,6 +337,7 @@
<script src="{{ url_for('static_bp.serve_js', filename='receipt_upload.js') }}?v={{ APP_VERSION }}"></script>
<script src="{{ url_for('static_bp.serve_js', filename='sort_mode.js') }}?v={{ APP_VERSION }}"></script>
<script src="{{ url_for('static_bp.serve_js', filename='access_users.js') }}?v={{ APP_VERSION }}"></script>
<script src="{{ url_for('static_bp.serve_js', filename='category_modal.js') }}?v={{ APP_VERSION }}"></script>
<script>
setupList({{ list.id }}, '{{ current_user.username if current_user.is_authenticated else 'Gość' }}');
</script>