mirror of https://github.com/logos-co/open-law.git
Merge pull request #91 from Simple2B/syvat/breadcrumbs
Syvat/breadcrumbs
This commit is contained in:
commit
3c4137052e
|
@ -11,6 +11,7 @@
|
||||||
"**/.venv/*/**": true
|
"**/.venv/*/**": true
|
||||||
},
|
},
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
|
"backref",
|
||||||
"bookname",
|
"bookname",
|
||||||
"Btns",
|
"Btns",
|
||||||
"CLEANR",
|
"CLEANR",
|
||||||
|
|
|
@ -5,11 +5,25 @@ from app import models as m, db
|
||||||
from app import schema as s
|
from app import schema as s
|
||||||
|
|
||||||
|
|
||||||
|
def create_collections_breadcrumb(
|
||||||
|
bread_crumbs: list[s.BreadCrumb], collection: m.Collection
|
||||||
|
) -> list[s.BreadCrumb]:
|
||||||
|
bread_crumbs += [
|
||||||
|
s.BreadCrumb(
|
||||||
|
type=s.BreadCrumbType.Collection,
|
||||||
|
url="",
|
||||||
|
label=collection.label,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
if collection.parent and not collection.parent.is_root:
|
||||||
|
create_collections_breadcrumb(bread_crumbs, collection.parent)
|
||||||
|
|
||||||
|
|
||||||
def create_breadcrumbs(
|
def create_breadcrumbs(
|
||||||
book_id: int,
|
book_id: int,
|
||||||
collection_path: tuple[int],
|
|
||||||
section_id: int = 0,
|
section_id: int = 0,
|
||||||
interpretation_id: int = 0,
|
collection_id: int = 0,
|
||||||
) -> list[s.BreadCrumb]:
|
) -> list[s.BreadCrumb]:
|
||||||
"""
|
"""
|
||||||
How breadcrumbs look like:
|
How breadcrumbs look like:
|
||||||
|
@ -52,39 +66,32 @@ def create_breadcrumbs(
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
for index, collection_id in enumerate(collection_path):
|
section: m.Section = None
|
||||||
if collection_id is None:
|
if section_id:
|
||||||
continue
|
|
||||||
collection: m.Collection = db.session.get(m.Collection, collection_id)
|
|
||||||
if index == 0:
|
|
||||||
crumples += [
|
|
||||||
s.BreadCrumb(
|
|
||||||
type=s.BreadCrumbType.Collection,
|
|
||||||
url="",
|
|
||||||
label=collection.label,
|
|
||||||
)
|
|
||||||
]
|
|
||||||
elif index == 1:
|
|
||||||
crumples += [
|
|
||||||
s.BreadCrumb(
|
|
||||||
type=s.BreadCrumbType.Section,
|
|
||||||
url="",
|
|
||||||
label=collection.label,
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
if section_id and collection_path:
|
|
||||||
section: m.Section = db.session.get(m.Section, section_id)
|
section: m.Section = db.session.get(m.Section, section_id)
|
||||||
|
|
||||||
|
if collection_id and not section:
|
||||||
|
collections_crumbs = []
|
||||||
|
collection: m.Collection = db.session.get(m.Collection, collection_id)
|
||||||
|
if not collection.is_root:
|
||||||
|
create_collections_breadcrumb(collections_crumbs, collection)
|
||||||
|
collections_crumbs.reverse()
|
||||||
|
crumples += collections_crumbs
|
||||||
|
|
||||||
|
if section:
|
||||||
|
collections_crumbs = []
|
||||||
|
collection: m.Collection = db.session.get(m.Collection, section.collection_id)
|
||||||
|
if not collection.is_root:
|
||||||
|
create_collections_breadcrumb(collections_crumbs, collection)
|
||||||
|
collections_crumbs.reverse()
|
||||||
|
crumples += collections_crumbs
|
||||||
|
|
||||||
crumples += [
|
crumples += [
|
||||||
s.BreadCrumb(
|
s.BreadCrumb(
|
||||||
type=s.BreadCrumbType.Section,
|
type=s.BreadCrumbType.Section,
|
||||||
url=url_for(
|
url=url_for(
|
||||||
"book.interpretation_view",
|
"book.interpretation_view",
|
||||||
book_id=book_id,
|
book_id=book_id,
|
||||||
collection_id=collection_path[0],
|
|
||||||
sub_collection_id=collection_path[-1]
|
|
||||||
if len(collection_path) == 2
|
|
||||||
else collection_path[0],
|
|
||||||
section_id=section_id,
|
section_id=section_id,
|
||||||
),
|
),
|
||||||
label=section.label,
|
label=section.label,
|
||||||
|
|
|
@ -6,6 +6,7 @@ from app.controllers import create_breadcrumbs
|
||||||
from .interpretation import Interpretation
|
from .interpretation import Interpretation
|
||||||
from .comment import Comment
|
from .comment import Comment
|
||||||
from .interpretation_vote import InterpretationVote
|
from .interpretation_vote import InterpretationVote
|
||||||
|
from app import schema as s
|
||||||
|
|
||||||
|
|
||||||
class Section(BaseModel):
|
class Section(BaseModel):
|
||||||
|
@ -46,16 +47,15 @@ class Section(BaseModel):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def breadcrumbs_path(self):
|
def breadcrumbs_path(self):
|
||||||
parent = self.collection
|
breadcrumbs_path = create_breadcrumbs(
|
||||||
grand_parent = parent.parent
|
book_id=self.book_id, collection_id=self.collection.id
|
||||||
if grand_parent.is_root:
|
)
|
||||||
collection_path = (parent.id,)
|
if len(breadcrumbs_path) > 5:
|
||||||
else:
|
breadcrumbs_path = (
|
||||||
collection_path = (
|
breadcrumbs_path[:3]
|
||||||
grand_parent.id,
|
+ [s.BreadCrumb(type=s.BreadCrumbType.Splitter, url="", label="...")]
|
||||||
parent.id,
|
+ breadcrumbs_path[-2:]
|
||||||
)
|
)
|
||||||
breadcrumbs_path = create_breadcrumbs(self.book_id, collection_path)
|
|
||||||
return breadcrumbs_path
|
return breadcrumbs_path
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -11,6 +11,7 @@ class BreadCrumbType(enum.StrEnum):
|
||||||
Collection = "Collection"
|
Collection = "Collection"
|
||||||
Section = "Section"
|
Section = "Section"
|
||||||
Interpretation = "Interpretation"
|
Interpretation = "Interpretation"
|
||||||
|
Splitter = "Splitter"
|
||||||
|
|
||||||
|
|
||||||
class BreadCrumb(BaseModel):
|
class BreadCrumb(BaseModel):
|
||||||
|
|
|
@ -32,12 +32,15 @@
|
||||||
<div data="sub-collection-context-menu-{{sub_collection.id}}" id="dropdown" class="z-10 hidden bg-white divide-y divide-gray-800 border border-gray-800 dark:border-none dark:divide-gray-100 rounded-lg shadow w-44 dark:bg-gray-700">
|
<div data="sub-collection-context-menu-{{sub_collection.id}}" id="dropdown" class="z-10 hidden bg-white divide-y divide-gray-800 border border-gray-800 dark:border-none dark:divide-gray-100 rounded-lg shadow w-44 dark:bg-gray-700">
|
||||||
{% if current_user.is_authenticated %}
|
{% if current_user.is_authenticated %}
|
||||||
<ul class="py-2 text-sm text-gray-700 dark:text-gray-200">
|
<ul class="py-2 text-sm text-gray-700 dark:text-gray-200">
|
||||||
|
{% if sub_collection.is_leaf and not sub_collection.children %}
|
||||||
<li>
|
<li>
|
||||||
<button type="button" id="callAddSectionModal" data-modal-target="add-section-modal" data-modal-toggle="add-section-modal" data-collection-id="{{collection.id}}" data-sub-collection-id="{{sub_collection.id}}" class="w-full block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"> New Section </button>
|
<button type="button" id="callAddSectionModal" data-modal-target="add-section-modal" data-modal-toggle="add-section-modal" data-collection-id="{{collection.id}}" data-sub-collection-id="{{sub_collection.id}}" class="w-full block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"> New Section </button>
|
||||||
</li>
|
</li>
|
||||||
|
{% else %}
|
||||||
<li>
|
<li>
|
||||||
<button type="button" id="callAddSubCollectionModal" data-modal-target="add-sub-collection-modal" data-modal-toggle="add-sub-collection-modal" data-collection-id="{{sub_collection.id}}" class="w-full block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"> New Subcollection </button>
|
<button type="button" id="callAddSubCollectionModal" data-modal-target="add-sub-collection-modal" data-modal-toggle="add-sub-collection-modal" data-collection-id="{{sub_collection.id}}" class="w-full block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"> New Subcollection </button>
|
||||||
</li>
|
</li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="py-2 text-sm text-gray-700 dark:text-gray-200">
|
<ul class="py-2 text-sm text-gray-700 dark:text-gray-200">
|
||||||
<li>
|
<li>
|
||||||
|
|
|
@ -7,17 +7,24 @@
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600 dark:text-gray-400 dark:hover:text-white" data-tooltip-target="breadcrumb-{{loop.index}}-tooltip" data-tooltip-placement="bottom">
|
<span class="inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600 dark:text-gray-400 dark:hover:text-white" data-tooltip-target="breadcrumb-{{loop.index}}-tooltip" data-tooltip-placement="bottom">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if breadcrumb.type != "Splitter" %}
|
||||||
<div id="breadcrumb-{{loop.index}}-tooltip" role="tooltip" class="delay-100 absolute z-[100] invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-700 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
|
<div id="breadcrumb-{{loop.index}}-tooltip" role="tooltip" class="delay-100 absolute z-[100] invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-700 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
|
||||||
{{ breadcrumb.label }}
|
{{ breadcrumb.label }}
|
||||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<!-- prettier-ignore -->
|
<!-- prettier-ignore -->
|
||||||
<span class="select-none">{{ breadcrumb.label }}</span>
|
<span class="select-none">{{ breadcrumb.label }}</span>
|
||||||
|
|
||||||
{% if not loop.index==local_breadcrumbs|length %}
|
{% if not loop.index==local_breadcrumbs|length %}
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% if not loop.index==local_breadcrumbs|length-1 %}
|
{% if not loop.index==local_breadcrumbs|length-1 %}
|
||||||
<svg aria-hidden="true" class="w-4 h-4 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>
|
<svg aria-hidden="true" class="w-4 h-4 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>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -21,7 +21,7 @@ from .bp import bp
|
||||||
@bp.route("/<int:book_id>/collections", methods=["GET"])
|
@bp.route("/<int:book_id>/collections", methods=["GET"])
|
||||||
def collection_view(book_id: int):
|
def collection_view(book_id: int):
|
||||||
book = db.session.get(m.Book, book_id)
|
book = db.session.get(m.Book, book_id)
|
||||||
breadcrumbs = create_breadcrumbs(book_id=book_id, collection_path=())
|
breadcrumbs = create_breadcrumbs(book_id=book_id)
|
||||||
if not book or book.is_deleted:
|
if not book or book.is_deleted:
|
||||||
log(log.WARNING, "Book with id [%s] not found", book_id)
|
log(log.WARNING, "Book with id [%s] not found", book_id)
|
||||||
flash("Book not found", "danger")
|
flash("Book not found", "danger")
|
||||||
|
|
|
@ -38,20 +38,14 @@ def interpretation_view(
|
||||||
book_id=book_id,
|
book_id=book_id,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# FIXME breadcrumbs
|
breadcrumbs = create_breadcrumbs(
|
||||||
# breadcrumbs = create_breadcrumbs(
|
book_id=book_id, section_id=section_id, collection_id=section.collection.id
|
||||||
# book_id=book_id,
|
)
|
||||||
# collection_path=(
|
|
||||||
# collection_id,
|
|
||||||
# sub_collection_id,
|
|
||||||
# ),
|
|
||||||
# section_id=section_id,
|
|
||||||
# )
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"book/interpretation_view.html",
|
"book/interpretation_view.html",
|
||||||
book=book,
|
book=book,
|
||||||
section=section,
|
section=section,
|
||||||
breadcrumbs=[],
|
breadcrumbs=breadcrumbs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -191,20 +185,15 @@ def qa_view(book_id: int, interpretation_id: int):
|
||||||
flash("Interpretation not found", "danger")
|
flash("Interpretation not found", "danger")
|
||||||
return redirect(url_for("book.collection_view", book_id=book_id))
|
return redirect(url_for("book.collection_view", book_id=book_id))
|
||||||
|
|
||||||
# FIXME
|
breadcrumbs = create_breadcrumbs(
|
||||||
# breadcrumbs = create_breadcrumbs(
|
book_id=book_id,
|
||||||
# book_id=book_id,
|
collection_id=interpretation.section.collection.id,
|
||||||
# collection_path=(
|
section_id=interpretation.section.id,
|
||||||
# collection_id,
|
)
|
||||||
# sub_collection_id,
|
|
||||||
# ),
|
|
||||||
# section_id=section_id,
|
|
||||||
# interpretation_id=interpretation.id,
|
|
||||||
# )
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"book/qa_view.html",
|
"book/qa_view.html",
|
||||||
book=book,
|
book=book,
|
||||||
section=interpretation.section,
|
section=interpretation.section,
|
||||||
interpretation=interpretation,
|
interpretation=interpretation,
|
||||||
breadcrumbs=[],
|
breadcrumbs=breadcrumbs,
|
||||||
)
|
)
|
||||||
|
|
|
@ -458,7 +458,7 @@ def test_crud_subcollection(client: FlaskClient, runner: FlaskCliRunner):
|
||||||
label="Test SubCollection #1 Label"
|
label="Test SubCollection #1 Label"
|
||||||
).first()
|
).first()
|
||||||
assert sub_collection
|
assert sub_collection
|
||||||
assert sub_collection.is_leaf
|
assert not sub_collection.is_leaf
|
||||||
assert sub_collection.parent_id == collection.id
|
assert sub_collection.parent_id == collection.id
|
||||||
|
|
||||||
m.Collection(
|
m.Collection(
|
||||||
|
|
|
@ -6,13 +6,13 @@ from app import models as m, db
|
||||||
def test_breadcrumbs(runner: FlaskCliRunner, app):
|
def test_breadcrumbs(runner: FlaskCliRunner, app):
|
||||||
runner.invoke(args=["db-populate"])
|
runner.invoke(args=["db-populate"])
|
||||||
with app.app_context(), app.test_request_context():
|
with app.app_context(), app.test_request_context():
|
||||||
res = create_breadcrumbs(1, (1,), 1)
|
res = create_breadcrumbs(book_id=1, collection_id=1, section_id=1)
|
||||||
assert len(res) == 4
|
assert len(res) == 4
|
||||||
book: m.Book = db.session.get(m.Book, 1)
|
book: m.Book = db.session.get(m.Book, 1)
|
||||||
assert book
|
assert book
|
||||||
assert book.owner.username in res[0].label
|
assert book.owner.username in res[0].label
|
||||||
assert res[1].label == book.label
|
assert res[1].label == book.label
|
||||||
with app.app_context(), app.test_request_context():
|
with app.app_context(), app.test_request_context():
|
||||||
res = create_breadcrumbs(1, (), 1)
|
res = create_breadcrumbs(book_id=1, section_id=1)
|
||||||
assert res
|
assert res
|
||||||
assert len(res) == 2
|
assert len(res) == 4
|
||||||
|
|
Loading…
Reference in New Issue