Dynamic object lists.
authorRadek Czajka <rczajka@rczajka.pl>
Fri, 9 Jun 2023 13:18:24 +0000 (15:18 +0200)
committerRadek Czajka <rczajka@rczajka.pl>
Fri, 9 Jun 2023 13:31:49 +0000 (15:31 +0200)
12 files changed:
src/catalogue/static/2022/book/filter.js
src/catalogue/templates/catalogue/2022/book_list.html [new file with mode: 0644]
src/catalogue/templates/catalogue/2022/dynamic_book_list.html [new file with mode: 0644]
src/catalogue/templates/catalogue/2022/dynamic_themed_book_list.html [new file with mode: 0644]
src/catalogue/templates/catalogue/2022/themed_book_list.html [new file with mode: 0644]
src/catalogue/urls.py
src/catalogue/views.py
src/experiments/experiments.py
src/search/templates/search/results.html
src/search/views.py
src/wolnelektury/static/2021/scripts/main.js
src/wolnelektury/static/2022/styles/layout/_pagination.scss

index bdf6357..59e810a 100644 (file)
@@ -1,5 +1,43 @@
 (function($) {
 
+    let unpagedSearch = null;
+    if (!$(".quick-filter").val() && !$('.l-pagination li').length) {
+        unpagedSearch = '';
+    }
+    
+    function get_page(page, search, ordering, callback) {
+        get_page_by_url('.?page=' + page + '&order=' + ordering + '&search=' + search, callback);
+    }
+    function get_page_by_url(url, callback) {
+        $.get(
+            url,
+            function(data) {
+                html = $(data);
+                objectList = $('#object-list', html);
+                paginate = $('#paginate', html);
+
+                ids = new Set(); 
+                $(".icon-like", objectList).each(
+                    (i, e)=>{
+                        ids.add($(e).attr('data-book'));
+                    }
+                );
+                ids = [...ids].join(',');
+                $.refreshLikes(ids);
+
+                $('#book-list').html(objectList.children());
+                $('#paginator').html(paginate.children());
+                history.replaceState({}, '', url);
+                callback && callback();
+            }
+        )
+    }
+
+    $("#paginator").on('click', 'a', function() {
+        get_page_by_url(url=$(this).attr('href'));
+        return false;
+    });
+
     $(".quick-filter").each(function() {
         let bookList = $('#' + $(this).data('for'));
         let filterList = $('.' + $(this).data('filters'));
         $(this).on('input propertychange', function() {
             let search = $(this).val().toLowerCase();
 
-            bookList.children().each(function() {
-                found = !search ||
-                    $(".s", this).text().toLowerCase().search(search) != -1
+            if (!search.startsWith(unpagedSearch)) {
+                get_page(1, search, 'title', function() {
+                    if ($('.l-pagination li').length) {
+                        unpagedSearch = null;
+                    }
+                })
+            } else {
+                bookList.children().each(function() {
+                    found = !search ||
+                        $(".s", this).text().toLowerCase().search(search) != -1
                     ;
-                if (found) 
-                    $(this).fadeIn();
-                else
-                    $(this).fadeOut();
-            });
-
-
+                    if (found) 
+                        $(this).fadeIn();
+                    else
+                        $(this).fadeOut();
+                });
+            }
 
             $('.filter-container', filterList).children().each(function() {
-                console.log($(this).text().toLowerCase());
                 found = !search ||
                     $(this).text().toLowerCase().search(search) != -1
                     ;
         $(".is-active", $(this).parent()).removeClass("is-active");
         $(this).addClass("is-active");
         let prop = $(this).attr('data-order');
+
+
+        // do we NOW have pages (possibly after filtering)?
+        // if we don't have pages, we can just sort here.
+        let havePages = $('.l-pagination li').length > 0;
+
         $(".l-books__item").css('opacity', '0');
         setTimeout(function() {
-            if (prop) {
-                $(".l-books__item").each(function() {
-                    $(this).css('order', $(this).attr(prop));
-                });
+            if (havePages) {
+                get_page(1, '', prop);
             } else {
-                $(".l-books__item").css('order', '');
+                if (prop) {
+                    $(".l-books__item").each(function() {
+                        $(this).css('order', $(this).attr('data-' + prop));
+                    });
+                } else {
+                    $(".l-books__item").css('order', '');
+                }
+                setTimeout(function() {
+                    $(".l-books__item").css('opacity', '100%');
+                }, 200);
             }
-            setTimeout(function() {
-                $(".l-books__item").css('opacity', '100%');
-            }, 200);
         }, 200);
     });
     
diff --git a/src/catalogue/templates/catalogue/2022/book_list.html b/src/catalogue/templates/catalogue/2022/book_list.html
new file mode 100644 (file)
index 0000000..e08cf3a
--- /dev/null
@@ -0,0 +1,126 @@
+{% extends '2022/base.html' %}
+{% load catalogue_tags %}
+{% load thumbnail %}
+{% load pagination_tags %}
+
+{% load choose_cites from social_tags %}
+
+
+{% block breadcrumbs %}
+  <a href="/katalog/"><span>Katalog</span></a>
+  {% if main_tag %}
+    <a href="{{ main_tag.get_absolute_catalogue_url }}"><span>{{ main_tag.get_category_display|title }}</span></a>
+  {% endif %}
+{% endblock %}
+
+{% block main %}
+  <div class="l-section">
+    <div class="l-author__header">
+      {% if main_tag.photo %}
+        {% thumbnail main_tag.photo '40x40' crop='center' as th %}
+        <figure>
+          <img src="{{ th.url }}" alt="{{ main_tag.name }}">
+        </figure>
+        {% endthumbnail %}
+      {% endif %}
+      <h1>
+        {% if main_tag %}
+          {{ main_tag.name }}
+        {% else %}
+          {{ view.title }}
+        {% endif %}
+      </h1>
+    </div>
+  </div>
+
+  <div class="l-section">
+    <div class="l-books__header">
+      <div class="l-books__input">
+        <i class="icon icon-filter"></i>
+        <input type="text" placeholder="filtry, tytuł" class="quick-filter" data-for="book-list" data-filters="with-filter" value="{{ request.GET.search }}">
+        <div class="filter-container">
+          {% for tag in tags %}
+            {% if tag is not main_tag %}
+              <span class="filter filter-category-{{ tag.category }}">
+                <a href="{% catalogue_url view.list_type tag %}">{{ tag }}</a>
+                <a href="{% catalogue_url view.list_type tags -tag %}">✖</a>
+              </span>
+            {% endif %}
+          {% endfor %}
+        </div>
+
+      </div>
+      {% if view.get_orderings %}
+      <div class="l-books__sorting">
+        <span>Sortuj:</span>
+        <div>
+          {% for ordering in view.get_orderings %}
+            <button
+                {% if not ordering.default %}
+                data-order="{{ ordering.slug }}"
+                {% endif %}
+                {% if ordering.active %}
+                class="is-active"
+                {% endif %}
+            >{{ ordering.name }}</button>
+          {% endfor %}
+        </div>
+      </div>
+      {% endif %}
+    </div>
+  </div>
+  <div class="l-author with-filter">
+    <div class="row">
+      <h2>{% nice_title_from_tags tags suggested_tags_by_category %}</h2>
+      {% if suggested_tags %}
+        <div class="filter-container">
+          {% for tag in suggested_tags %}
+            <span class="filter filter-category-{{ tag.category }}">
+              <a href="{% catalogue_url view.list_type tags tag %}">{{ tag }}</a>
+            </span>
+          {% endfor %}
+        </div>
+      {% endif %}
+    </div>
+  </div>
+
+  {% autopaginate object_list view.page_size %}
+
+  <div class="l-section l-section--col">
+    <div class="l-books__grid" id="book-list">
+      {% for book in object_list %}
+        {% include "catalogue/2022/book_box.html" %}
+      {% endfor %}
+    </div>
+  </div>
+
+  <div id="paginator">
+    {% paginate using '2022/paginate.html' %}
+  </div>
+
+  {% if main_tag %}
+    <section class="l-section">
+      <div class="l-author">
+        {% with tag=main_tag %}
+          {% include 'catalogue/2022/author_box.html' %}
+        {% endwith %}
+        {% choose_cites 3 author=main_tag as cites %}
+        {% if cites %}
+          <div class="row">
+            <div class="l-author__quotes">
+              <div class="l-author__quotes__slider">
+
+                {% for fragment in cites %}
+                  <div class="l-author__quotes__slider__item">
+                    {% include "catalogue/2022/fragment_slider_box.html" %}
+                  </div>
+                {% endfor %}
+
+              </div>
+            </div>
+          </div>
+        {% endif %}
+      </div>
+    </section>
+    {% endif %}
+{% endblock %}
diff --git a/src/catalogue/templates/catalogue/2022/dynamic_book_list.html b/src/catalogue/templates/catalogue/2022/dynamic_book_list.html
new file mode 100644 (file)
index 0000000..0511be5
--- /dev/null
@@ -0,0 +1,14 @@
+{% load pagination_tags %}
+{% autopaginate object_list view.page_size %}
+
+<div>
+<div id="object-list">
+  {% for book in object_list %}
+    {% include "catalogue/2022/book_box.html" %}
+  {% endfor %}
+</div>
+
+<div id="paginate">
+  {% paginate using '2022/paginate.html' %}
+</div>
+</div>
diff --git a/src/catalogue/templates/catalogue/2022/dynamic_themed_book_list.html b/src/catalogue/templates/catalogue/2022/dynamic_themed_book_list.html
new file mode 100644 (file)
index 0000000..3346d88
--- /dev/null
@@ -0,0 +1,14 @@
+{% load pagination_tags %}
+{% autopaginate object_list view.themed_page_size %}
+
+<div>
+<div id="object-list">
+  {% for fragment in object_list %}
+    {% include "catalogue/2022/fragment_box.html" %}
+  {% endfor %}
+</div>
+
+<div id="paginate">
+  {% paginate using '2022/paginate.html' %}
+</div>
+</div>
diff --git a/src/catalogue/templates/catalogue/2022/themed_book_list.html b/src/catalogue/templates/catalogue/2022/themed_book_list.html
new file mode 100644 (file)
index 0000000..8ba5e58
--- /dev/null
@@ -0,0 +1,90 @@
+{% extends '2022/base.html' %}
+{% load catalogue_tags %}
+{% load pagination_tags %}
+
+{% block global-content %}
+  <div class="l-container">
+    <div class="l-breadcrumb">
+      <a href="/"><span>Strona główna</span></a>
+      <a href="/katalog/"><span>Katalog</span></a>
+      <a href="/katalog/motyw/"><span>Motyw</span></a>
+      <span>{{ main_tag.name }}</span>
+    </div>
+  </div>
+
+  <main class="l-main">
+
+    <div class="l-section">
+      <div class="l-author__header">
+        <h1><span>Motyw:</span> {{ main_tag.name }}</h1>
+      </div>
+    </div>
+
+
+    <div class="l-section">
+      <div class="l-books__header">
+        <div class="l-books__input">
+          <i class="icon icon-filter"></i>
+          <input type="text" placeholder="filtry, tytuł" class="quick-filter" data-for="book-list" data-filters="with-filter" value="{{ request.GET.search }}">
+          <div class="filter-container">
+            {% for tag in tags %}
+              {% if tag is not main_tag %}
+                <span class="filter filter-category-{{ tag.category }}">
+                  <a href="{% catalogue_url view.list_type tag %}">{{ tag }}</a>
+                  <a href="{% catalogue_url view.list_type tags -tag %}">✖</a>
+                </span>
+              {% endif %}
+            {% endfor %}
+          </div>
+
+        </div>
+      </div>
+    </div>
+    <div class="l-author with-filter">
+      <div class="row">
+        <h2>{% nice_title_from_tags tags categories %}</h2>
+        {% if suggest %}
+          <div class="filter-container">
+            {% for tag in suggest %}
+              <span class="filter filter-category-{{ tag.category }}">
+                <a href="{% catalogue_url view.list_type tags tag %}">{{ tag }}</a>
+              </span>
+            {% endfor %}
+          </div>
+        {% endif %}
+      </div>
+    </div>
+
+    {% autopaginate object_list view.themed_page_size %}
+
+    <div class="l-section l-section--col">
+      <div class="l-theme">
+        <div class="l-theme__col">
+          <div class="l-books__col" id="book-list">
+            {% for fragment in object_list %}
+              {% include 'catalogue/2022/fragment_box.html' %}
+            {% endfor %}
+          </div>
+          <div id="paginator">
+            {% paginate using '2022/paginate.html' %}
+          </div>
+        </div>
+        <div class="l-theme__col">
+          <div class="l-theme__info">
+            <h3>Motyw: {{ main_tag.name }}</h3>
+            {{ main_tag.description|safe }}
+            <!--
+                 <h3>Motyw w sztuce <i class="icon icon-arrow-left"></i> <i class="icon icon-arrow-right"></i></h3>
+                 <div class="l-theme__info__slider">
+                 <img src="images/motyw.jpg" alt="">
+                 <img src="images/motyw.jpg" alt="">
+                 <img src="images/motyw.jpg" alt="">
+                 </div>
+            -->
+          </div>
+        </div>
+      </div>
+    </div>
+  </main>
+
+{% endblock %}
index 7049b1c..840d2bf 100644 (file)
@@ -30,12 +30,12 @@ urlpatterns = [
     path('rodzaj/', views.tag_catalogue, {'category': 'kind'}, name='kind_catalogue'),
     path('motyw/', views.tag_catalogue, {'category': 'theme'}, name='theme_catalogue'),
 
-    path('galeria/', views.gallery, name='gallery'),
+    path('galeria/', views.GalleryView.as_view(), name='gallery'),
     path('kolekcje/', views.collections, name='catalogue_collections'),
 
-    path('lektury/', views.literature, name='book_list'),
+    path('lektury/', views.LiteratureView.as_view(), name='book_list'),
     path('lektury/<slug:slug>/', views.collection, name='collection'),
-    path('audiobooki/', views.audiobooks, name='audiobook_list'),
+    path('audiobooki/', views.AudiobooksView.as_view(), name='audiobook_list'),
     path('daisy/', views.daisy_list, name='daisy_list'),
     path('jtags/', search.views.hint, {'param': 'q', 'mozhint': True}, name='jhint'),
     path('nowe/', ListView.as_view(
@@ -76,7 +76,7 @@ urlpatterns = [
         name='tagged_object_list_gallery'),
     re_path(r'^audiobooki/(?P<tags>[a-zA-Z0-9-/]*)/$', views.tagged_object_list, {'list_type': 'audiobooks'},
         name='tagged_object_list_audiobooks'),
-    re_path(r'^(?P<tags>[a-zA-Z0-9-/]*)/$', views.tagged_object_list, {'list_type': 'books'},
+    re_path(r'^(?P<tags>[a-zA-Z0-9-/]*)/$', views.TaggedObjectList.as_view(),
         name='tagged_object_list'),
 
 ]
index e027200..2d8aea7 100644 (file)
@@ -16,6 +16,7 @@ from django.contrib.auth.decorators import login_required, user_passes_test
 from django.utils import translation
 from django.utils.translation import gettext as _, gettext_lazy
 from django.views.decorators.cache import never_cache
+from django.views.generic import TemplateView
 
 from ajaxable.utils import AjaxableFormView
 from club.forms import ScheduleForm, DonationStep1Form
@@ -23,10 +24,12 @@ from club.models import Club
 from annoy.models import DynamicTextInsert
 from pdcounter import views as pdcounter_views
 from picture.models import Picture, PictureArea
+from wolnelektury.utils import is_ajax
 from catalogue import constants
 from catalogue import forms
 from catalogue.helpers import get_top_level_related_tags
 from catalogue.models import Book, Collection, Tag, Fragment
+from catalogue.models.tag import TagRelation
 from catalogue.utils import split_tags
 from catalogue.models.tag import prefetch_relations
 
@@ -95,6 +98,212 @@ def differentiate_tags(request, tags, ambiguous_slugs):
     )
 
 
+from django.db.models import FilteredRelation, Q
+from django.views.decorators.cache import cache_control
+from django.views.decorators.vary import vary_on_headers
+from django.utils.decorators import method_decorator
+
+
+@method_decorator([
+    vary_on_headers('X-Requested-With'),
+    cache_control(max_age=1),
+], 'dispatch')
+class ObjectListView(TemplateView):
+    page_size = 100
+    themed_page_size = 10
+    item_template_name = ''
+    orderings = {}
+    default_ordering = None
+
+    def setup(self, request, **kwargs):
+        super().setup(request, **kwargs)
+        self.is_themed = False
+        self.ctx = ctx = {}
+        ctx['tags'] = []        
+
+    def get_orderings(self):
+        order = self.get_order()
+        return [
+            {
+                "slug": k,
+                "name": v[1],
+                "active": k == order,
+                "default": v[0] is None,
+            }
+            for k, v in self.orderings.items()
+        ]
+
+    def get_order(self):
+        order = self.request.GET.get('order')
+        if order not in self.orderings:
+            order = self.default_ordering
+        return order
+
+    def order(self, qs):
+        order_tag = self.get_order()
+        if order_tag:
+            order = self.orderings[order_tag]
+            order_by = order[0]
+            if order_by:
+                qs = qs.order_by(order_by)
+        return qs
+
+    def search(self, qs):
+        return qs
+
+    def get_template_names(self):
+        if is_ajax(self.request) or self.request.GET.get('dyn'):
+            if self.is_themed:
+                return self.dynamic_themed_template_name
+            else:
+                return self.dynamic_template_name
+        else:
+            if self.is_themed:
+                return self.themed_template_name
+            else:
+                return self.template_name
+
+    def get_context_data(self, **kwargs):
+        ctx = super().get_context_data()
+        ctx.update(self.ctx)
+        qs = self.get_queryset()
+        qs = self.search(qs)
+        qs = self.order(qs)
+
+        ctx['object_list'] = qs
+        ctx['suggested_tags'] = self.get_suggested_tags(qs)
+        ctx['suggested_tags_by_category'] = split_tags(ctx['suggested_tags'])
+        return ctx
+
+
+class BookList(ObjectListView):
+    title = gettext_lazy('Literature')
+    list_type = 'books'
+    template_name = 'catalogue/2022/book_list.html'
+    dynamic_template_name = 'catalogue/2022/dynamic_book_list.html'
+    themed_template_name = 'catalogue/2022/themed_book_list.html'
+    dynamic_themed_template_name = 'catalogue/2022/dynamic_themed_book_list.html'
+
+    orderings = {
+        'pop': ('-popularity__count', 'najpopularniejsze'),
+        'alpha': (None, 'alfabetycznie'),
+    }
+    default_ordering = 'alpha'
+
+    def get_queryset(self):
+        return Book.objects.filter(parent=None, findable=True)
+
+    def search(self, qs):
+        term = self.request.GET.get('search')
+        if term:
+            meta_rels = TagRelation.objects.exclude(tag__category='set')
+            # TODO: search tags in currently displaying language
+            if self.is_themed:
+                #qs = qs.annotate(
+                #    meta=FilteredRelation('book__tag_relations', condition=Q(tag_relations__in=meta_rels))
+                #)
+                qs = qs.filter(
+                    Q(book__title__icontains=term) |
+                    #Q(meta__tag_relations__tag__name_pl__icontains=term) |
+                    Q(text__icontains=term)
+                ).distinct()
+            else:
+                qs = qs.annotate(
+                    meta=FilteredRelation('tag_relations', condition=Q(tag_relations__in=meta_rels))
+                )
+                qs = qs.filter(Q(title__icontains=term) | Q(meta__tag__name_pl__icontains=term)).distinct()
+        return qs
+
+
+class ArtList(ObjectListView):
+    template_name = 'catalogue/2022/book_list.html'
+    dynamic_template_name = 'catalogue/2022/dynamic_book_list.html'
+    title = gettext_lazy('Art')
+    list_type = 'gallery'
+
+    def get_queryset(self):
+        return Picture.objects.all()
+
+    def search(self, qs):
+        term = self.request.GET.get('search')
+        if term:
+            qs = qs.filter(Q(title__icontains=term) | Q(tag_relations__tag__name_pl__icontains=term)).distinct()
+        return qs
+    
+
+class LiteratureView(BookList):
+    def get_suggested_tags(self, queryset):
+        tags = list(get_top_level_related_tags([]))
+        tags.sort(key=lambda t: -t.count)
+        if self.request.user.is_authenticated:
+            tags.extend(list(Tag.objects.filter(user=self.request.user).exclude(name='')))
+        return tags
+
+
+class AudiobooksView(LiteratureView):
+    title = gettext_lazy('Audiobooks')
+
+    def get_queryset(self):
+        return Book.objects.filter(findable=True, media__type='mp3').distinct()
+
+
+class GalleryView(ArtList):
+    def get_suggested_tags(self, queryset):
+        return Tag.objects.usage_for_queryset(
+            queryset,
+            counts=True
+        ).exclude(pk__in=[t.id for t in self.ctx['tags']]).order_by('-count')
+    
+
+class TaggedObjectList(BookList):
+    def setup(self, request, tags, **kwargs):
+        super().setup(request, **kwargs)
+        self.ctx['tags'] = analyse_tags(request, tags)
+        self.ctx['fragment_tags'] = [t for t in self.ctx['tags'] if t.category in ('theme', 'object')]
+        self.ctx['work_tags'] = [t for t in self.ctx['tags'] if t not in self.ctx['fragment_tags']]
+        self.is_themed = self.ctx['has_theme'] = bool(self.ctx['fragment_tags'])
+        self.ctx['main_tag'] = self.ctx['fragment_tags'][0] if self.is_themed else self.ctx['tags'][0]
+        self.ctx['filtering_tags'] = [
+            t for t in self.ctx['tags']
+            if t is not self.ctx['main_tag']
+        ]
+
+    def get_queryset(self):
+        qs = Book.tagged.with_all(self.ctx['work_tags']).filter(findable=True)
+        qs = qs.exclude(ancestor__in=qs)
+        if self.is_themed:
+            qs = Fragment.tagged.with_all(self.ctx['fragment_tags']).filter(
+                Q(book__in=qs) | Q(book__ancestor__in=qs)
+            )
+        return qs
+
+    def get_suggested_tags(self, queryset):
+        tag_ids = [t.id for t in self.ctx['tags']]
+        related_tags = list(get_top_level_related_tags(self.ctx['tags']))
+        if not self.is_themed:
+            if self.request.user.is_authenticated:
+                qs = Book.tagged.with_all(self.ctx['tags']).filter(findable=True)
+                related_tags.extend(list(
+                    Tag.objects.usage_for_queryset(
+                        qs
+                    ).filter(
+                        user=self.request.user
+                    ).exclude(name='').exclude(pk__in=tag_ids)
+                ))
+
+            fragments = Fragment.objects.filter(
+                Q(book__in=queryset) | Q(book__ancestor__in=queryset)
+            )
+            related_tags.extend(
+                Tag.objects.usage_for_queryset(
+                    fragments, counts=True
+                ).filter(category__in=('theme', 'object')).exclude(pk__in=tag_ids)
+            .only('name', 'sort_key', 'category', 'slug'))
+
+        return related_tags
+
+    
+    
 def object_list(request, objects, fragments=None, related_tags=None, tags=None,
                 list_type='books', extra=None):
     if not tags:
@@ -135,7 +344,7 @@ def object_list(request, objects, fragments=None, related_tags=None, tags=None,
     categories = split_tags(*related_tag_lists)
     suggest = []
     for c in ['set', 'author', 'epoch', 'kind', 'genre']:
-        suggest.extend(sorted(categories[c], key=lambda t: -t.count)[:3])
+        suggest.extend(sorted(categories[c], key=lambda t: -t.count))
 
     objects = list(objects)
 
index 60d0521..d38666d 100644 (file)
@@ -9,10 +9,6 @@ class NewLayout(Experiment):
     name = 'Nowy layout strony'
     size = settings.EXPERIMENTS_LAYOUT
 
-    def qualify(self, request):
-        if get_language() != 'pl':
-            return False
-
 
 class Sowka(Experiment):
     slug = 'sowka'
@@ -21,14 +17,7 @@ class Sowka(Experiment):
     switchable = False
 
 
-class Search(Experiment):
-    slug = 'search'
-    name = 'Nowa wyszukiwarka'
-    size = settings.EXPERIMENTS_SEARCH
-
-
 experiments = [
     NewLayout,
     Sowka,
-    Search
 ]
index dbd0d6f..625bb65 100644 (file)
     {% endif %}
 
     {% if results.author %}
-    <div class="l-container">
-      <h2 class="header">Autorzy</h2>
-      <ul class="c-search-result c-search-result-author">
-        {% for tag in results.author %}
-          <li>
-            <a href="{{ tag.get_absolute_url }}">
-              {% if tag.photo %}
-                <figure>
-                  <img src="{{ tag.photo.url }}">
-                </figure>
-              {% endif %}
-              {{ tag.name }}
-            </a>
-          </li>
-        {% endfor %}
-      </ul>
-    </div>
+      <div class="l-container">
+        <h2 class="header">Autorzy</h2>
+        <ul class="c-search-result c-search-result-author">
+          {% for tag in results.author %}
+            <li>
+              <a href="{{ tag.get_absolute_url }}">
+                {% if tag.photo %}
+                  <figure>
+                    <img src="{{ tag.photo.url }}">
+                  </figure>
+                {% endif %}
+                {{ tag.name }}
+              </a>
+            </li>
+          {% endfor %}
+        </ul>
+      </div>
     {% endif %}
 
     {% if results.theme %}
       </div>
     {% endif %}
 
+    {% if results.genre %}
+      <div class="l-container">
+        <h2 class="header">Gatunki</h2>
+        <ul class="c-search-result">
+          {% for tag in results.genre %}
+            <li>
+              <a href="{{ tag.get_absolute_url }}">
+                {% if tag.photo %}
+                  <figure>
+                    <img src="{{ tag.photo.url }}">
+                  </figure>
+                {% endif %}
+                {{ tag.name }}
+              </a>
+            </li>
+          {% endfor %}
+        </ul>
+      </div>
+    {% endif %}
+
     {% if results.book %}
       <div class="l-container">
         <h2 class="header">Książki</h2>
index 6ff0f7a..9e36458 100644 (file)
@@ -137,8 +137,7 @@ def search(request):
 
 @cache.never_cache
 def main(request):
-    if request.EXPERIMENTS['search'].value:
-        request.EXPERIMENTS['layout'].override(True)
+    if request.EXPERIMENTS['layout'].value:
         return search(request)
 
     query = request.GET.get('q', '')
index 843cddb..3bbb38d 100644 (file)
         liked: [],
     };
     
-    $(".icon-like").on('click', function(e) {
+    $(document).on('click', '.icon-like', function(e) {
         e.preventDefault();
         let liked = $(this).hasClass('icon-liked');
         $btn = $(this);
         }
     })
 
+    // TODO: DYNAMICALLY ADD
    $(".add-set-tag input[name=name]").autocomplete({
        source: '/ludzie/moje-tagi/',
    }).on('autocompleteopen', function() {
         });
     }
     refreshAll(ids);
+    $.refreshLikes = refreshAll;
 
     function updateFromData(data) {
         for (pk in data) {
index d243670..ec8ace7 100644 (file)
@@ -1,3 +1,7 @@
+#paginator {
+    display: flex;
+}
+
 .l-pagination {
   display: flex;
   align-content: center;