feat: location filter, mobile overflow fixes, card scan icon
- Replace indoor/outdoor filter with dynamic location buttons - Fix pruning strip date/badge overflow on narrow screens (flex-shrink-0, gap-2, text-nowrap, flex-wrap) - Fix pruning calendar badges compressing on long plant names - Re-add pruning strip to plant detail (was missing) - Change card scan nav icon from 📷 to 🪪 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
21f9759d3a
commit
b5cdda54f8
7 changed files with 28 additions and 19 deletions
|
|
@ -30,7 +30,7 @@
|
||||||
<a class="navbar-brand fw-bold" href="{% url 'dashboard' %}">🌸 BloomBase</a>
|
<a class="navbar-brand fw-bold" href="{% url 'dashboard' %}">🌸 BloomBase</a>
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<a href="{% url 'plant_list' %}" class="btn btn-sm btn-outline-light">Plants</a>
|
<a href="{% url 'plant_list' %}" class="btn btn-sm btn-outline-light">Plants</a>
|
||||||
<a href="{% url 'add_plant_from_scan' %}" class="btn btn-sm btn-outline-light" title="Add plant by card scan">📷</a>
|
<a href="{% url 'add_plant_from_scan' %}" class="btn btn-sm btn-outline-light" title="Add plant by card scan">🪪</a>
|
||||||
<a href="{% url 'identify_upload' %}" class="btn btn-sm btn-light fw-bold">+</a>
|
<a href="{% url 'identify_upload' %}" class="btn btn-sm btn-light fw-bold">+</a>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@
|
||||||
<div class="fw-semibold" style="color:#1b4332;">{{ plant.name }}</div>
|
<div class="fw-semibold" style="color:#1b4332;">{{ plant.name }}</div>
|
||||||
<small class="text-muted">
|
<small class="text-muted">
|
||||||
{{ plant.location }}
|
{{ plant.location }}
|
||||||
· {% if plant.is_indoor %}Indoor{% else %}Outdoor{% endif %}
|
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{% load plant_extras %}
|
{% load plant_extras %}
|
||||||
<div id="pruning-strip" class="pruning-strip {% if pruning_status == 'overdue' %}overdue{% endif %} mb-3 rounded-end">
|
<div id="pruning-strip" class="pruning-strip {% if pruning_status == 'overdue' %}overdue{% endif %} mb-3 rounded-end">
|
||||||
<div class="d-flex justify-content-between align-items-start">
|
<div class="d-flex justify-content-between align-items-start gap-2">
|
||||||
<div>
|
<div>
|
||||||
<strong>✂️ Pruning</strong>
|
<strong>✂️ Pruning</strong>
|
||||||
{% if plant.pruning_months %}
|
{% if plant.pruning_months %}
|
||||||
|
|
@ -8,15 +8,17 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% with plant.pruning_logs.first as last_log %}
|
{% with plant.pruning_logs.first as last_log %}
|
||||||
{% if last_log %}
|
{% if last_log %}
|
||||||
<div class="small text-muted mt-1">Last pruned: {{ last_log.pruned_on|date:"j M Y" }}</div>
|
<div class="small text-muted mt-1 text-nowrap">Last pruned: {{ last_log.pruned_on|date:"j M Y" }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex-shrink-0">
|
||||||
{% if pruning_status == 'overdue' %}
|
{% if pruning_status == 'overdue' %}
|
||||||
<span class="badge bg-danger">Overdue</span>
|
<span class="badge bg-danger">Overdue</span>
|
||||||
{% elif pruning_status == 'due_this_month' %}
|
{% elif pruning_status == 'due_this_month' %}
|
||||||
<span class="badge bg-warning text-dark">Due this month</span>
|
<span class="badge bg-warning text-dark">Due this month</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form
|
<form
|
||||||
|
|
@ -26,7 +28,7 @@
|
||||||
class="mt-2"
|
class="mt-2"
|
||||||
>
|
>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="d-flex gap-2 align-items-end">
|
<div class="d-flex gap-2 align-items-end flex-wrap">
|
||||||
{{ log_form.pruned_on }}
|
{{ log_form.pruned_on }}
|
||||||
<button type="submit" class="btn btn-sm btn-success">Log pruning</button>
|
<button type="submit" class="btn btn-sm btn-success">Log pruning</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,10 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if plant.pruning_months %}
|
||||||
|
{% include "plants/partials/pruning_strip.html" %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div id="photo-gallery">
|
<div id="photo-gallery">
|
||||||
{% include "plants/partials/photo_gallery.html" %}
|
{% include "plants/partials/photo_gallery.html" %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,11 @@
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="btn-group w-100 mb-3" role="group">
|
<div class="btn-group w-100 mb-3 flex-wrap" role="group">
|
||||||
<a href="{% url 'plant_list' %}" class="btn btn-sm {% if not filter %}btn-success{% else %}btn-outline-success{% endif %}">All</a>
|
<a href="{% url 'plant_list' %}" class="btn btn-sm {% if not location_filter %}btn-success{% else %}btn-outline-success{% endif %}">All</a>
|
||||||
<a href="{% url 'plant_list' %}?filter=indoor" class="btn btn-sm {% if filter == 'indoor' %}btn-success{% else %}btn-outline-success{% endif %}">Indoor</a>
|
{% for loc in locations %}
|
||||||
<a href="{% url 'plant_list' %}?filter=outdoor" class="btn btn-sm {% if filter == 'outdoor' %}btn-success{% else %}btn-outline-success{% endif %}">Outdoor</a>
|
<a href="{% url 'plant_list' %}?location={{ loc.pk }}" class="btn btn-sm {% if location_filter == loc.pk|stringformat:'s' %}btn-success{% else %}btn-outline-success{% endif %}">{{ loc.name }}</a>
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="plant-list">
|
<div id="plant-list">
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
<div class="fw-semibold">{{ plant.name }}</div>
|
<div class="fw-semibold">{{ plant.name }}</div>
|
||||||
<small>{{ plant.location }}</small>
|
<small>{{ plant.location }}</small>
|
||||||
</div>
|
</div>
|
||||||
<span class="badge bg-danger">Overdue</span>
|
<span class="badge bg-danger flex-shrink-0">Overdue</span>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
<div class="fw-semibold">{{ plant.name }}</div>
|
<div class="fw-semibold">{{ plant.name }}</div>
|
||||||
<small>{{ plant.location }}</small>
|
<small>{{ plant.location }}</small>
|
||||||
</div>
|
</div>
|
||||||
<span class="badge bg-warning text-dark">This month</span>
|
<span class="badge bg-warning text-dark flex-shrink-0">This month</span>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -42,7 +42,7 @@
|
||||||
<div class="fw-semibold">{{ plant.name }}</div>
|
<div class="fw-semibold">{{ plant.name }}</div>
|
||||||
<small>{{ plant.location }}</small>
|
<small>{{ plant.location }}</small>
|
||||||
</div>
|
</div>
|
||||||
<span class="badge bg-secondary">{{ next_month_name }}</span>
|
<span class="badge bg-secondary flex-shrink-0">{{ next_month_name }}</span>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from datetime import date
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||||
from django.views.decorators.http import require_POST, require_GET
|
from django.views.decorators.http import require_POST, require_GET
|
||||||
from plants.models import Plant, Species, PruningLog, PlantPhoto, PlantCardPhoto
|
from plants.models import Plant, Species, PruningLog, PlantPhoto, PlantCardPhoto, Location
|
||||||
from plants.forms import PlantForm, PruningLogForm
|
from plants.forms import PlantForm, PruningLogForm
|
||||||
from plants.utils.pruning import pruning_status
|
from plants.utils.pruning import pruning_status
|
||||||
|
|
||||||
|
|
@ -10,21 +10,24 @@ from plants.utils.pruning import pruning_status
|
||||||
def plant_list(request):
|
def plant_list(request):
|
||||||
qs = Plant.objects.select_related('species', 'location').prefetch_related('photos').all()
|
qs = Plant.objects.select_related('species', 'location').prefetch_related('photos').all()
|
||||||
q = request.GET.get('q', '')
|
q = request.GET.get('q', '')
|
||||||
indoor_filter = request.GET.get('filter', '')
|
location_filter = request.GET.get('location', '')
|
||||||
|
|
||||||
if q:
|
if q:
|
||||||
qs = qs.filter(name__icontains=q) | qs.filter(location__name__icontains=q)
|
qs = qs.filter(name__icontains=q) | qs.filter(location__name__icontains=q)
|
||||||
if indoor_filter == 'indoor':
|
if location_filter:
|
||||||
qs = qs.filter(is_indoor=True)
|
qs = qs.filter(location__pk=location_filter)
|
||||||
elif indoor_filter == 'outdoor':
|
|
||||||
qs = qs.filter(is_indoor=False)
|
|
||||||
|
|
||||||
|
locations = Location.objects.all()
|
||||||
template = (
|
template = (
|
||||||
'plants/partials/plant_list_results.html'
|
'plants/partials/plant_list_results.html'
|
||||||
if request.htmx
|
if request.htmx
|
||||||
else 'plants/plant_list.html'
|
else 'plants/plant_list.html'
|
||||||
)
|
)
|
||||||
return render(request, template, {'plants': qs, 'q': q, 'filter': indoor_filter})
|
return render(request, template, {
|
||||||
|
'plants': qs, 'q': q,
|
||||||
|
'location_filter': location_filter,
|
||||||
|
'locations': locations,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def plant_detail(request, pk):
|
def plant_detail(request, pk):
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue