kolory kategorii i jedniklikowy wybor kategorii w modalu
This commit is contained in:
43
app.py
43
app.py
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
|
18
static/js/category_modal.js
Normal file
18
static/js/category_modal.js
Normal 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();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
@@ -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>
|
||||
|
Reference in New Issue
Block a user