blog-browse #40
|
@ -32,11 +32,17 @@
|
|||
</div>
|
||||
<div class="col-sm-3">
|
||||
<div class="container-fluid w-100 h-100 p-2">
|
||||
<form class="w-100 h-100" role="search" action="" method="GET" novalidate>
|
||||
<form class="w-100 h-100"
|
||||
role="search"
|
||||
action=""
|
||||
method="GET"
|
||||
novalidate
|
||||
id="filter-form">
|
||||
<div class="py-2">
|
||||
<input type="search"
|
||||
name="search"
|
||||
class="form-control flex-fill py-2"
|
||||
defaultValue=""
|
||||
aria-label="Search"
|
||||
placeholder="{% trans "Search" %}"
|
||||
required=""
|
||||
|
@ -46,6 +52,7 @@
|
|||
<div class="py-2">
|
||||
<select class="form-select py-2"
|
||||
aria-label="Large select example"
|
||||
defaultValue=""
|
||||
name="category">
|
||||
<option value="">{% trans "select category" %}</option>
|
||||
{% for category in categories %}
|
||||
|
@ -57,6 +64,7 @@
|
|||
<div class="py-2">
|
||||
<input class="tagify"
|
||||
name="keywords"
|
||||
defaultValue=""
|
||||
placeholder="{% trans "Keywords" %}"
|
||||
{% if filters.keywords %} value="{% for keyword in filters.keywords %}{{ keyword }} {% endfor %}
|
||||
"
|
||||
|
@ -65,9 +73,11 @@
|
|||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="py-2">
|
||||
<input id="clear-button"
|
||||
type="reset"
|
||||
class="btn bg-primary fw-bold py-2 float-end w-100">
|
||||
<!-- will reset to the filters encoded in
|
||||
the url by GET attributes, not to the empty
|
||||
form. -->
|
||||
<input type="reset"
|
||||
class="btn bg-primary fw-bold py-2 float-end w-100 reset-empty-button">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
|
@ -91,8 +101,15 @@
|
|||
<div class="row mb-5">
|
||||
<div class="container-fluid p-0 w-100 h-100">
|
||||
<div class="row gap-3">
|
||||
{# TODO: display some info if the queryset is empty #}
|
||||
{# TODO: pagination #}
|
||||
{% if is_empty %}
|
||||
<div class="text-center">
|
||||
<img src="/media/img/http/404.svg"
|
||||
class="img-fluid pb-5 pt-2 darkmode-invert"
|
||||
style="max-height: 650px;"
|
||||
alt="404" />
|
||||
<h2 class="display-5">{% trans "No posts found for your filters." %}</h2>
|
||||
</div>
|
||||
{% else %}
|
||||
{% for post in posts %}
|
||||
<div class="card col mx-auto my-2 py-2" style="min-width: 400px;">
|
||||
<a class="text-reset link-offset-2 link-underline link-underline-opacity-0"
|
||||
|
@ -146,6 +163,7 @@
|
|||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
from django.shortcuts import Http404, HttpResponse, get_object_or_404, render
|
||||
import json
|
||||
import ast
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.http.response import Http404, HttpResponse
|
||||
from django.http.request import HttpRequest
|
||||
from django.utils.translation import get_language
|
||||
from django.views.generic import TemplateView, DetailView, ListView, View
|
||||
from django.db.models import Q
|
||||
|
@ -9,8 +13,6 @@ from start.views import SearchableView
|
|||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
import ast
|
||||
import json
|
||||
|
||||
class Index(TemplateView, SearchableView):
|
||||
"""
|
||||
|
@ -55,6 +57,7 @@ class Browse(ListView):
|
|||
model = BlogPost
|
||||
template_name = "blog/browse.html"
|
||||
context_object_name = "posts"
|
||||
allow_empty = False # but we have a special get method
|
||||
|
||||
def get_queryset(self):
|
||||
objects = BlogPost.objects.all()
|
||||
|
@ -123,3 +126,27 @@ class Browse(ListView):
|
|||
keywords = keywords.split('+')
|
||||
context["filters"]["keywords"] = keywords
|
||||
return context
|
||||
|
||||
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
self.object_list = self.get_queryset()
|
||||
allow_empty = self.get_allow_empty()
|
||||
|
||||
if not allow_empty:
|
||||
# When pagination is enabled and object_list is a queryset,
|
||||
# it's better to do a cheap query than to load the unpaginated
|
||||
# queryset in memory.
|
||||
if self.get_paginate_by(self.object_list) is not None and hasattr(
|
||||
self.object_list, 'exists'):
|
||||
is_empty = not self.object_list.exists()
|
||||
else:
|
||||
is_empty = not self.object_list
|
||||
else:
|
||||
is_empty = False
|
||||
context = self.get_context_data()
|
||||
|
||||
context["is_empty"] = is_empty
|
||||
|
||||
response = self.render_to_response(context)
|
||||
if is_empty:
|
||||
response.status_code = 404
|
||||
return response
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="557.15094"
|
||||
height="210.8916"
|
||||
viewBox="0 0 557.15094 210.8916"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:export-filename="404.svg"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="px" />
|
||||
<defs
|
||||
id="defs1">
|
||||
<rect
|
||||
x="292.2478"
|
||||
y="154.0531"
|
||||
width="462.1593"
|
||||
height="240.14159"
|
||||
id="rect1" />
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-239.50485,-280.19422)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
id="text1"
|
||||
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:24.5524px;font-family:'Source Code Pro';-inkscape-font-specification:'Source Code Pro, Bold Italic';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;white-space:pre;shape-inside:url(#rect1);fill:#3b3b3b;fill-opacity:1"
|
||||
transform="matrix(13.034066,0,0,13.034066,-3571.9156,-1800.7033)"><tspan
|
||||
x="292.24805"
|
||||
y="175.53608"
|
||||
id="tspan2">404</tspan></text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
|
@ -2,6 +2,18 @@
|
|||
// dark theme stuff
|
||||
const setTheme = (theme) => {
|
||||
document.documentElement.setAttribute("data-bs-theme", theme);
|
||||
if (theme === "dark") {
|
||||
var to_invert = document.querySelectorAll('.darkmode-invert');
|
||||
to_invert.forEach(element => {
|
||||
element.style.filter = "invert(100%)";
|
||||
});
|
||||
}
|
||||
if (theme === "light") {
|
||||
var to_invert = document.querySelectorAll('.darkmode-invert');
|
||||
to_invert.forEach(element => {
|
||||
element.style.filter = "invert(0%)";
|
||||
});
|
||||
}
|
||||
};
|
||||
setTheme(localStorage.getItem("theme"));
|
||||
document.getElementById("toggleThemeButton").onclick = function () {
|
||||
|
|
Loading…
Reference in New Issue