Merge pull request #16 from Simple2B/kostia/feture/bread_crumbs_navigation

Kostia/feture/bread crumbs navigation
This commit is contained in:
Svyatoslav Artymovych 2023-05-02 10:29:04 +03:00 committed by GitHub
commit 9c4f8962f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 317 additions and 140 deletions

View File

@ -1,2 +1,3 @@
# flake8: noqa F401
from .pagination import create_pagination
from .breadcrumbs import create_breadcrumbs

View File

@ -0,0 +1,131 @@
from flask import url_for
from flask_login import current_user
from app import models as m, db
from app import schema as s
def create_breadcrumbs(
book_id: int,
collection_path: tuple[int],
section_id: int = 0,
interpretation_id: int = 0,
) -> list[s.BreadCrumb]:
"""
How breadcrumbs look like:
Book List -> Book Name -> Top Level Collection -> SubCollection -> Section -> Interpretation
- If i am not owner of a book
John's books -> Book Name -> Top Level Collection -> SubCollection -> Section -> Interpretation
- If i am owner
My Books -> Book Title -> Part I -> Chapter X -> Paragraph 1.7 -> By John
"""
crumples: list[s.BreadCrumb] = []
book: m.Book = db.session.get(m.Book, book_id)
if current_user.is_authenticated and book.user_id == current_user.id:
# My Book
crumples += [
s.BreadCrumb(
type=s.BreadCrumbType.MyBookList,
url=url_for("book.my_books"),
label="My Books",
)
]
else:
# Not mine book
crumples += [
s.BreadCrumb(
type=s.BreadCrumbType.AuthorBookList,
url="#",
label=book.owner.username + "'s books",
)
]
crumples += [
s.BreadCrumb(
type=s.BreadCrumbType.Collection,
url=url_for("book.collection_view", book_id=book_id),
label=book.label,
)
]
for collection_id in collection_path:
if collection_id is None:
continue
collection: m.Collection = db.session.get(m.Collection, collection_id)
crumples += [
s.BreadCrumb(
type=s.BreadCrumbType.Collection,
url=url_for(
"book.sub_collection_view",
book_id=book_id,
collection_id=collection_id,
),
label=collection.label,
)
]
if section_id and collection_path:
section: m.Section = db.session.get(m.Section, section_id)
if len(collection_path) == 2:
crumples += [
s.BreadCrumb(
type=s.BreadCrumbType.Section,
url=url_for(
"book.section_view",
book_id=book_id,
collection_id=collection_path[0],
sub_collection_id=collection_path[-1],
),
label=section.label,
)
]
else:
crumples += [
s.BreadCrumb(
type=s.BreadCrumbType.Section,
url=url_for(
"book.section_view",
book_id=book_id,
collection_id=collection_path[0],
sub_collection_id=collection_path[0],
),
label=section.label,
)
]
if interpretation_id:
interpretation: m.Interpretation = db.session.get(
m.Interpretation, interpretation_id
)
if len(collection_path) == 2:
crumples += [
s.BreadCrumb(
type=s.BreadCrumbType.Interpretation,
url=url_for(
"book.interpretation_view",
book_id=book_id,
collection_id=collection_path[0],
sub_collection_id=collection_path[-1],
section_id=section_id,
),
label=interpretation.label,
)
]
else:
crumples += [
s.BreadCrumb(
type=s.BreadCrumbType.Interpretation,
url=url_for(
"book.interpretation_view",
book_id=book_id,
collection_id=collection_path[0],
sub_collection_id=collection_path[0],
section_id=section_id,
),
label=interpretation.label,
)
]
return crumples

View File

@ -1,3 +1,4 @@
# flake8: noqa F401
from .pagination import Pagination
from .user import User
from .breadcrumbs import BreadCrumbType, BreadCrumb

21
app/schema/breadcrumbs.py Normal file
View File

@ -0,0 +1,21 @@
import enum
from pydantic import BaseModel
class BreadCrumbType(enum.StrEnum):
"""Bread Crumb Type"""
MyBookList = "MyBookList"
AuthorBookList = "AuthorBookList"
Collection = "Collection"
Section = "Section"
Interpretation = "Interpretation"
class BreadCrumb(BaseModel):
"""Bread Crumb for navigation"""
label: str
url: str
type: BreadCrumbType

File diff suppressed because one or more lines are too long

View File

@ -22,9 +22,6 @@
<button id="search-btn" class="cursor-pointer select-none text-white absolute right-2.5 bottom-2.5 bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-4 py-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Search</button>
</div>
</div>
<div class="col-span-6 sm:col-span-3 overflow-x-auto shadow-md sm:rounded-lg" id="search-results">
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<tbody id="search-results-tbody">
@ -38,9 +35,7 @@
</tbody>
</table>
</div>
</div>
<div>
<label for="role" class="mb-2 block text-sm font-medium text-gray-900 dark:text-white">Select an option</label >
<select id="role" name="role" class="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">

View File

@ -0,0 +1,21 @@
<!-- prettier-ignore -->
<nav class="flex mt-5 mb-3" aria-label="Breadcrumb">
<ol class="inline-flex items-center space-x-1 md:space-x-3 ml-5">
{% for breadcrumb in breadcrumbs %}
<li class="inline-flex items-center">
<a href="{{ breadcrumb.url }}" class="inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600 dark:text-gray-400 dark:hover:text-white">
<!-- prettier-ignore -->
<!--svg for all types of breadcrumb-->
{% if breadcrumb.type == "MyBookList" or breadcrumb.type == "AuthorBookList" %}
<svg aria-hidden="true" class="flex-shrink-0 w-4 h-4 mr-2 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd"></path></svg>
{% else %}
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="flex-shrink-0 w-4 h-4 mr-2 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25" /> </svg>
{% endif %}
<!-- prettier-ignore -->
{{ breadcrumb.label }}
</a>
<svg aria-hidden="true" class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path> </svg>
</li>
{% endfor %}
</ol>
</nav>

View File

@ -18,13 +18,8 @@
{% block content %}
<div class="relative overflow-x-auto shadow-md sm:rounded-lg mt-5 md:mr-64">
<!-- prettier-ignore -->
<div class="flex p-2">
<div>
<h1 class="text-l font-extrabold dark:text-white ml-4"> {{ book.owner.username }} </h1>
<h1 class="text-2xl font-extrabold dark:text-white ml-4">{{ book.label }}</h1>
</div>
</div>
{% include 'book/breadcrumbs_navigation.html'%}
<h1 class="text-l font-extrabold dark:text-white ml-4">Collections page</h1>
<!-- prettier-ignore -->
<div class="mb-1 border-b border-gray-200 dark:border-gray-700">
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center" id="myTab" data-tabs-toggle="#myTabContent" role="tablist">
@ -43,16 +38,11 @@
</ul>
</div>
<div id="myTabContent">
<div
class="hidden p-4 rounded-lg bg-gray-50 dark:bg-gray-800"
id="files"
role="tabpanel"
aria-labelledby="files-tab">
<dl
class="w-md md:w-full text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700">
<!-- prettier-ignore -->
<div class="hidden p-4 rounded-lg bg-gray-50 dark:bg-gray-800" id="files" role="tabpanel" aria-labelledby="files-tab">
<dl class="w-md md:w-full text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700">
<!-- prettier-ignore -->
{% for collection in book.versions[-1].children_collections if not collection.is_root and not collection.is_deleted %}
<!-- prettier-ignore -->
<a href="{{url_for('book.sub_collection_view',book_id=book.id,collection_id=collection.id)}}" >
<dl class="bg-white dark:bg-gray-900 max-w-full p-3 text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700 m-3 border-2 border-gray-200 border-solid rounded-lg dark:border-gray-700">
@ -74,7 +64,6 @@
</div>
</dl>
</a >
{% endfor %}
</dl>
</div>

View File

@ -15,14 +15,10 @@
{% for book in books %}
<!-- prettier-ignore -->
<dl
class=" bg-white dark:bg-gray-900 max-w-full p-5 text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700 m-3 border-2 border-gray-200 border-solid rounded-lg dark:border-gray-700">
<a
class="flex flex-col pb-2"
href="{{url_for('book.collection_view',book_id=book.id)}}">
<dl class=" bg-white dark:bg-gray-900 max-w-full p-5 text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700 m-3 border-2 border-gray-200 border-solid rounded-lg dark:border-gray-700">
<a class="flex flex-col pb-2" href="{{url_for('book.collection_view',book_id=book.id)}}">
<dt class="mb-2">{{book.owner.username}}/{{book.label}}</dt>
<dd
class="flex flex-col md:flex-row text-lg font-semibold text-gray-500 md:text-lg dark:text-gray-400">
<dd class="flex flex-col md:flex-row text-lg font-semibold text-gray-500 md:text-lg dark:text-gray-400">
{% if book.versions %}
<p>
Last updated on {{book.versions[-1].updated_at.strftime('%B %d, %Y')}}
@ -30,48 +26,15 @@
{% endif %}
<div class="flex ml-auto align-center justify-center space-x-3">
<span class="space-x-0.5 flex items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 22 22"
stroke-width="1"
stroke="currentColor"
class="w-4 h-4 inline-flex mr-1">
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z" /> </svg>
<p>{{ book.stars|length }}</p>
</span>
<span class="space-x-0.5 flex items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 22 22"
stroke-width="1"
stroke="currentColor"
class="w-4 h-4 inline-flex mr-1">
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z" /> </svg>
<p>55</p>
</span>
<span class="space-x-0.5 flex items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 22 22"
stroke-width="1"
stroke="currentColor"
class="w-4 h-4 inline-flex mr-1">
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" /> </svg>
<p>55</p>
</span>
</div>
@ -79,6 +42,60 @@
</a>
</dl>
{% endfor %}
<!-- prettier-ignore -->
{% if page.pages > 1 %}
<div class="container content-center mt-3 flex bg-white dark:bg-gray-800">
<nav aria-label="Page navigation example" class="mx-auto">
<ul class="inline-flex items-center -space-x-px">
<li>
<!-- prettier-ignore -->
<a href="{{ url_for('book.my_books') }}?page=1&q={{page.query}}" class="block px-3 py-2 ml-0 leading-tight text-gray-500 bg-white border border-gray-300 rounded-l-lg hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">
<span class="sr-only">First</span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5"> <path fill-rule="evenodd" d="M15.79 14.77a.75.75 0 01-1.06.02l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 111.04 1.08L11.832 10l3.938 3.71a.75.75 0 01.02 1.06zm-6 0a.75.75 0 01-1.06.02l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 111.04 1.08L5.832 10l3.938 3.71a.75.75 0 01.02 1.06z" clip-rule="evenodd" /> </svg>
</a>
</li>
<li>
<!-- prettier-ignore -->
<a href="{{ url_for('book.my_books') }}?page={{page.page-1 if page.page > 1 else 1}}&q={{page.query}}" class="block px-3 py-2 ml-0 leading-tight text-gray-500 bg-white border border-gray-300 rounded-l-lg hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">
<span class="sr-only">Previous</span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5"> <path fill-rule="evenodd" d="M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z" clip-rule="evenodd" /> </svg>
</a>
</li>
<!-- prettier-ignore -->
{% for p in page.pages_for_links %}
<li>
<!-- prettier-ignore -->
{% if p == page.page %}
<!-- prettier-ignore -->
<a href="{{ url_for('book.my_books') }}?page={{p}}&q={{page.query}}" aria-current="page" class="z-10 px-3 py-2 leading-tight text-blue-600 border border-blue-300 bg-blue-50 hover:bg-blue-100 hover:text-blue-700 dark:border-gray-700 dark:bg-gray-700 dark:text-white">{{p}}</a>
{% else %}
<!-- prettier-ignore -->
<a href="{{ url_for('book.my_books') }}?page={{p}}&q={{page.query}}" class="px-3 py-2 leading-tight text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">{{p}}</a>
{% endif %}
</li>
{% endfor %}
<li>
<!-- prettier-ignore -->
<a href="{{ url_for('book.my_books') }}?page={{page.page+1 if page.page < page.pages else page.pages}}&q={{page.query}}" class="block px-3 py-2 leading-tight text-gray-500 bg-white border border-gray-300 rounded-r-lg hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">
<!-- prettier-ignore -->
<span class="sr-only">Next</span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5"> <path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /> </svg>
</a>
</li>
<li>
<!-- prettier-ignore -->
<a href="{{ url_for('book.my_books') }}?page={{page.pages}}&q={{page.query}}" class="block px-3 py-2 leading-tight text-gray-500 bg-white border border-gray-300 rounded-r-lg hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">
<!-- prettier-ignore -->
<span class="sr-only">Last</span>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="w-5 h-5"> <path fill-rule="evenodd" d="M10.21 14.77a.75.75 0 01.02-1.06L14.168 10 10.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /> <path fill-rule="evenodd" d="M4.21 14.77a.75.75 0 01.02-1.06L8.168 10 4.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" /> </svg>
</a>
</li>
</ul>
</nav>
</div>
{% endif %}
</div>
{% include 'user/add.html' %}
<!-- prettier-ignore -->

View File

@ -2,19 +2,11 @@
{% extends 'base.html' %}
{% block content %}
<div class="relative overflow-x-auto shadow-md sm:rounded-lg mt-5 md:mr-64">
{% include 'book/breadcrumbs_navigation.html'%}
<h1 class="text-l font-extrabold dark:text-white ml-4">
Interpretations page
</h1>
<!-- prettier-ignore -->
<div class="flex p-2">
<div>
<h1 class="text-l font-extrabold dark:text-white ml-4">
{{ book.owner.username }}/{{ book.label }}
</h1>
<h1 class="text-2xl font-extrabold dark:text-white ml-4">
{{collection.label}} {% if sub_collection %}/ {{sub_collection.label}} {% endif %} /{{section.label}}
</h1>
</div>
</div>
<!-- prettier-ignore -->
<div class="mb-1 border-b border-gray-200 dark:border-gray-700">
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center" id="myTab" data-tabs-toggle="#myTabContent" role="tablist">
<li class="mr-2" role="presentation">

View File

@ -1,6 +1,5 @@
<!-- prettier-ignore -->
{% extends 'base.html' %}
{% if book.owner.id == current_user.id %}
<!-- show edit collection btn on rightside bar -->
{% set show_edit_collection = True %}
@ -19,24 +18,21 @@
{% block content %}
<div class="relative overflow-x-auto shadow-md sm:rounded-lg mt-5 md:mr-64">
<!-- prettier-ignore -->
<div class="flex p-2">
<div>
<h1 class="text-l font-extrabold dark:text-white ml-4"> {{ book.owner.username }}/{{ book.label }} </h1>
<h1 class="text-2xl font-extrabold dark:text-white ml-4">{{collection.label}}{% if sub_collection %}/ {{sub_collection.label}} {% endif %} </h1>
</div>
</div>
<!-- prettier-ignore -->
{% include 'book/breadcrumbs_navigation.html'%}
<h1 class="text-l font-extrabold dark:text-white ml-4">Sections page</h1>
<div class="mb-1 border-b border-gray-200 dark:border-gray-700">
<!-- prettier-ignore -->
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center" id="myTab" data-tabs-toggle="#myTabContent" role="tablist">
<li class="mr-2" role="presentation">
<!-- prettier-ignore -->
<button class="flex items-center space-x-2 p-4 border-b-2 rounded-t-lg" id="files-tab" data-tabs-target="#files" type="button" role="tab" aria-controls="files" aria-selected="false">
<!-- prettier-ignore -->
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m5.231 13.481L15 17.25m-4.5-15H5.625c-.621 0-1.125.504-1.125 1.125v16.5c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9zm3.75 11.625a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z" /> </svg>
<span>Files</span>
</button>
</li>
<li class="mr-2" role="presentation">
<!-- prettier-ignore -->
<button class="flex items-center space-x-2 p-4 border-b-2 border-transparent rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300" id="about-tab" data-tabs-target="#about" type="button" role="tab" aria-controls="about" aria-selected="false">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> <path stroke-linecap="round" stroke-linejoin="round" d="M8.625 12a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H8.25m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H12m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0h-.375M21 12c0 4.556-4.03 8.25-9 8.25a9.764 9.764 0 01-2.555-.337A5.972 5.972 0 015.41 20.97a5.969 5.969 0 01-.474-.065 4.48 4.48 0 00.978-2.025c.09-.457-.133-.901-.467-1.226C3.93 16.178 3 14.189 3 12c0-4.556 4.03-8.25 9-8.25s9 3.694 9 8.25z" /> </svg>
<span>About</span>
@ -46,13 +42,9 @@
</div>
<div id="myTabContent">
<div
class="hidden p-4 rounded-lg bg-gray-50 dark:bg-gray-800"
id="files"
role="tabpanel"
aria-labelledby="files-tab">
<dl
class="w-md md:w-full text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700">
<!-- prettier-ignore -->
<div class="hidden p-4 rounded-lg bg-gray-50 dark:bg-gray-800" id="files" role="tabpanel" aria-labelledby="files-tab">
<dl class="w-md md:w-full text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700">
<!-- prettier-ignore -->
{% for section in sections %}
@ -72,7 +64,6 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z" /></svg>
<p>55</p>
</span>
<span class="space-x-0.5 flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" stroke-width="1" stroke="currentColor" class="w-4 h-4 inline-flex mr-1"> <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" /></svg>
<p>55</p>
@ -82,15 +73,10 @@
</div>
</dl>
</a>
{% endfor %}
</dl>
</div>
<div
class="hidden p-4 rounded-lg bg-gray-50 dark:bg-gray-800"
id="about"
role="tabpanel"
aria-labelledby="about-tab">
<div class="hidden p-4 rounded-lg bg-gray-50 dark:bg-gray-800" id="about" role="tabpanel" aria-labelledby="about-tab">
<p class="text-sm text-gray-500 dark:text-gray-400">This is about</p>
</div>
</div>

View File

@ -22,24 +22,23 @@
{% block content %}
<div class="relative overflow-x-auto shadow-md sm:rounded-lg mt-5 md:mr-64">
<!-- prettier-ignore -->
<div class="flex p-2">
<div>
<h1 class="text-l font-extrabold dark:text-white ml-4"> {{ book.owner.username }}/{{ book.label }} </h1>
<h1 class="text-2xl font-extrabold dark:text-white ml-4">{{collection.label}}</h1>
</div>
</div>
<!-- prettier-ignore -->
{% include 'book/breadcrumbs_navigation.html'%}
<h1 class="text-l font-extrabold dark:text-white ml-4">
Sub collections page
</h1>
<div class="mb-1 border-b border-gray-200 dark:border-gray-700">
<!-- prettier-ignore -->
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center" id="myTab" data-tabs-toggle="#myTabContent" role="tablist">
<li class="mr-2" role="presentation">
<!-- prettier-ignore -->
<button class="flex items-center space-x-2 p-4 border-b-2 rounded-t-lg" id="files-tab" data-tabs-target="#files" type="button" role="tab" aria-controls="files" aria-selected="false">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m5.231 13.481L15 17.25m-4.5-15H5.625c-.621 0-1.125.504-1.125 1.125v16.5c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9zm3.75 11.625a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z" /> </svg>
<span>Files</span>
</button>
</li>
<li class="mr-2" role="presentation">
<!-- prettier-ignore -->
<button class="flex items-center space-x-2 p-4 border-b-2 border-transparent rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300" id="about-tab" data-tabs-target="#about" type="button" role="tab" aria-controls="about" aria-selected="false">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> <path stroke-linecap="round" stroke-linejoin="round" d="M8.625 12a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H8.25m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H12m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0h-.375M21 12c0 4.556-4.03 8.25-9 8.25a9.764 9.764 0 01-2.555-.337A5.972 5.972 0 015.41 20.97a5.969 5.969 0 01-.474-.065 4.48 4.48 0 00.978-2.025c.09-.457-.133-.901-.467-1.226C3.93 16.178 3 14.189 3 12c0-4.556 4.03-8.25 9-8.25s9 3.694 9 8.25z" /> </svg>
<span>About</span>
@ -49,22 +48,17 @@
</div>
<div id="myTabContent">
<div
class="hidden p-4 rounded-lg bg-gray-50 dark:bg-gray-800"
id="files"
role="tabpanel"
aria-labelledby="files-tab">
<!-- prettier-ignore -->
<div class="hidden p-4 rounded-lg bg-gray-50 dark:bg-gray-800" id="files" role="tabpanel" aria-labelledby="files-tab">
<!-- prettier-ignore -->
<dl class="w-md md:w-full text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700">
<!-- prettier-ignore -->
{% for sub_collection in collection.children %}
<!-- prettier-ignore -->
<a href="{{url_for('book.section_view',book_id=book.id,collection_id=collection.id,sub_collection_id=sub_collection.id)}}">
<dl class="bg-white dark:bg-gray-900 max-w-full p-3 text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700 m-3 border-2 border-gray-200 border-solid rounded-lg dark:border-gray-700">
<div class="flex flex-col pb-3 p-3 w-full">
<dt
class="flex w-full mb-1 text-gray-500 md:text-lg dark:text-gray-400 flex-col">
<dt class="flex w-full mb-1 text-gray-500 md:text-lg dark:text-gray-400 flex-col">
<p>{{ sub_collection.label }}</p>
<div class="ml-auto">
<!-- prettier-ignore -->
@ -80,17 +74,13 @@
{% endfor %}
</dl>
</div>
<div
class="hidden p-4 rounded-lg bg-gray-50 dark:bg-gray-800"
id="about"
role="tabpanel"
aria-labelledby="about-tab">
<!-- prettier-ignore -->
<div class="hidden p-4 rounded-lg bg-gray-50 dark:bg-gray-800" id="about" role="tabpanel" aria-labelledby="about-tab">
<p class="text-sm text-gray-500 dark:text-gray-400">
This is about of {{collection.about}}
</p>
</div>
</div>
<!-- prettier-ignore -->
{% endblock %}
<!-- prettier-ignore -->

View File

@ -69,7 +69,7 @@
<!-- prettier-ignore -->
<h1 class="text-l font-extrabold dark:text-white my-2">{{section.path}}</h1>
{% endfor %}
<a type="button" href="{{ url_for('book.get_all') }}" class="mt-4 text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"> Explore all sections... <svg aria-hidden="true" class="w-5 h-5 ml-2 -mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg> </a>
<a type="button" href="#" class="mt-4 text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"> Explore all sections... <svg aria-hidden="true" class="w-5 h-5 ml-2 -mr-1" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg> </a>
</div>
</div>
{% endblock %}

View File

@ -35,17 +35,8 @@
<a
href="{{ url_for('user.get_all') }}"
class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
<svg
aria-hidden="true"
class="flex-shrink-0 w-6 h-6 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
clip-rule="evenodd"></path>
</svg>
<!-- prettier-ignore -->
<svg aria-hidden="true" class="flex-shrink-0 w-6 h-6 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd"></path> </svg>
<span class="flex-1 ml-3 whitespace-nowrap">Users</span>
</a>
</li>

View File

@ -8,7 +8,7 @@ from flask import (
)
from flask_login import login_required, current_user
from app.controllers import create_pagination
from app.controllers import create_pagination, create_breadcrumbs
from app import models as m, db, forms as f
from app.logger import log
@ -101,12 +101,15 @@ def edit(book_id: int):
@bp.route("/<int:book_id>/collections", methods=["GET"])
def collection_view(book_id: int):
book = db.session.get(m.Book, book_id)
breadcrumbs = create_breadcrumbs(book_id=book_id, collection_path=())
if not book or book.is_deleted:
log(log.WARNING, "Book with id [%s] not found", book_id)
flash("Book not found", "danger")
return redirect(url_for("book.my_books"))
else:
return render_template("book/collection_view.html", book=book)
return render_template(
"book/collection_view.html", book=book, breadcrumbs=breadcrumbs
)
@bp.route("/<int:book_id>/<int:collection_id>/subcollections", methods=["GET"])
@ -116,19 +119,22 @@ def sub_collection_view(book_id: int, collection_id: int):
log(log.WARNING, "Book with id [%s] not found", book_id)
flash("Book not found", "danger")
return redirect(url_for("book.my_books"))
collection: m.Collection = db.session.get(m.Collection, collection_id)
if not collection or collection.is_deleted:
log(log.WARNING, "Collection with id [%s] not found", collection_id)
flash("Collection not found", "danger")
return redirect(url_for("book.collection_view", book_id=book_id))
breadcrumbs = create_breadcrumbs(book_id=book_id, collection_path=(collection.id,))
if collection.is_leaf:
return redirect(
url_for("book.section_view", book_id=book.id, collection_id=collection.id)
)
else:
return render_template(
"book/sub_collection_view.html", book=book, collection=collection
"book/sub_collection_view.html",
book=book,
collection=collection,
breadcrumbs=breadcrumbs,
)
@ -171,12 +177,21 @@ def section_view(
else:
sections = collection.sections
breadcrumbs = create_breadcrumbs(
book_id=book_id,
collection_path=(
collection_id,
sub_collection_id,
),
)
return render_template(
"book/section_view.html",
book=book,
collection=collection,
sections=sections,
sub_collection=sub_collection,
breadcrumbs=breadcrumbs,
)
@ -232,12 +247,21 @@ def interpretation_view(
)
)
else:
breadcrumbs = create_breadcrumbs(
book_id=book_id,
collection_path=(
collection_id,
sub_collection_id,
),
section_id=section_id,
)
return render_template(
"book/interpretation_view.html",
book=book,
collection=collection,
sub_collection=sub_collection if sub_collection_id else None,
section=section,
breadcrumbs=breadcrumbs,
)

18
tests/test_breadcrumbs.py Normal file
View File

@ -0,0 +1,18 @@
from flask.testing import FlaskCliRunner
from app.controllers import create_breadcrumbs
from app import models as m, db
def test_breadcrumbs(runner: FlaskCliRunner, app):
runner.invoke(args=["db-populate"])
with app.app_context(), app.test_request_context():
res = create_breadcrumbs(1, (1,), 1)
assert len(res) == 4
book: m.Book = db.session.get(m.Book, 1)
assert book
assert book.owner.username in res[0].label
assert res[1].label == book.label
with app.app_context(), app.test_request_context():
res = create_breadcrumbs(1, (), 1)
assert res
assert len(res) == 2