mirror of
https://github.com/logos-co/open-law.git
synced 2025-01-09 14:25:58 +00:00
Merge branch 'develop' into svyat/feat/user_permissions
This commit is contained in:
commit
1554015e31
@ -22,7 +22,6 @@ def create_app(environment="development"):
|
||||
user_blueprint,
|
||||
book_blueprint,
|
||||
home_blueprint,
|
||||
section_blueprint,
|
||||
vote_blueprint,
|
||||
approve_blueprint,
|
||||
star_blueprint,
|
||||
@ -55,7 +54,6 @@ def create_app(environment="development"):
|
||||
app.register_blueprint(user_blueprint)
|
||||
app.register_blueprint(book_blueprint)
|
||||
app.register_blueprint(home_blueprint)
|
||||
app.register_blueprint(section_blueprint)
|
||||
app.register_blueprint(vote_blueprint)
|
||||
app.register_blueprint(approve_blueprint)
|
||||
app.register_blueprint(star_blueprint)
|
||||
|
@ -39,7 +39,7 @@ def create_breadcrumbs(
|
||||
crumples += [
|
||||
s.BreadCrumb(
|
||||
type=s.BreadCrumbType.AuthorBookList,
|
||||
url="#",
|
||||
url="",
|
||||
label=book.owner.username + "'s books",
|
||||
)
|
||||
]
|
||||
@ -60,11 +60,7 @@ def create_breadcrumbs(
|
||||
crumples += [
|
||||
s.BreadCrumb(
|
||||
type=s.BreadCrumbType.Collection,
|
||||
url=url_for(
|
||||
"book.sub_collection_view",
|
||||
book_id=book_id,
|
||||
collection_id=collection_id,
|
||||
),
|
||||
url="",
|
||||
label=collection.label,
|
||||
)
|
||||
]
|
||||
@ -72,12 +68,7 @@ def create_breadcrumbs(
|
||||
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],
|
||||
),
|
||||
url="",
|
||||
label=collection.label,
|
||||
)
|
||||
]
|
||||
|
@ -28,6 +28,8 @@ def delete_nested_version_entities(book_version: m.BookVersion):
|
||||
|
||||
|
||||
def delete_nested_collection_entities(collection: m.Collection):
|
||||
for sub_collection in collection.children:
|
||||
delete_nested_collection_entities(sub_collection)
|
||||
for section in collection.sections:
|
||||
section: m.Section
|
||||
section.is_deleted = True
|
||||
|
@ -25,7 +25,7 @@ def set_book_tags(book: m.Book, tags: str):
|
||||
for book_tag in book_tags:
|
||||
db.session.delete(book_tag)
|
||||
|
||||
tags_names = [tag.title() for tag in tags.split(",") if len(tag)]
|
||||
tags_names = [tag.lower() for tag in tags.split(",") if len(tag)]
|
||||
|
||||
for tag_name in tags_names:
|
||||
try:
|
||||
|
@ -1,7 +1,10 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, SubmitField
|
||||
from wtforms import StringField, SubmitField, ValidationError
|
||||
from wtforms.validators import DataRequired
|
||||
|
||||
from app.controllers import clean_html
|
||||
from app.logger import log
|
||||
|
||||
|
||||
class BaseInterpretationForm(FlaskForm):
|
||||
about = StringField("About")
|
||||
@ -12,6 +15,14 @@ class CreateInterpretationForm(BaseInterpretationForm):
|
||||
section_id = StringField("Interpretation ID", [DataRequired()])
|
||||
submit = SubmitField("Create")
|
||||
|
||||
def validate_text(self, field):
|
||||
text = clean_html(field.data)
|
||||
text = text.replace(" ", "")
|
||||
text = text.strip()
|
||||
if len(text) < 1:
|
||||
log(log.WARNING, "Can't submit empty interpretation")
|
||||
raise ValidationError("You can't create interpretation with no text")
|
||||
|
||||
|
||||
class EditInterpretationForm(BaseInterpretationForm):
|
||||
interpretation_id = StringField("Interpretation ID", [DataRequired()])
|
||||
|
@ -1,3 +1,5 @@
|
||||
import base64
|
||||
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import (
|
||||
StringField,
|
||||
@ -58,7 +60,7 @@ class NewUserForm(FlaskForm):
|
||||
|
||||
class EditUserForm(FlaskForm):
|
||||
name = StringField("Name", [DataRequired()])
|
||||
avatar_img = FileField("Avatar file (max 200x200px)")
|
||||
avatar_img = FileField("Avatar file (max 1mb, formats: jpg,jpeg,png)")
|
||||
submit = SubmitField("Save")
|
||||
|
||||
def validate_username(self, field):
|
||||
@ -69,6 +71,16 @@ class EditUserForm(FlaskForm):
|
||||
):
|
||||
raise ValidationError("This username is taken.")
|
||||
|
||||
def validate_avatar_img(self, field):
|
||||
if field.data:
|
||||
img_data = field.data.read()
|
||||
img_data = base64.b64encode(img_data)
|
||||
img_data = img_data.decode("utf-8")
|
||||
field.data = img_data
|
||||
size = len(img_data) / 1000000
|
||||
if size > 1:
|
||||
raise ValidationError("Avatar file size too large")
|
||||
|
||||
|
||||
class ReactivateUserForm(FlaskForm):
|
||||
submit = SubmitField("Save")
|
||||
|
@ -36,4 +36,8 @@ class BookVersion(BaseModel):
|
||||
|
||||
@property
|
||||
def children_collections(self):
|
||||
return self.root_collection.children
|
||||
return [
|
||||
collection
|
||||
for collection in self.root_collection.children
|
||||
if not collection.is_deleted
|
||||
]
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,7 +1,10 @@
|
||||
{% extends "base.html" %}
|
||||
<!-- prettier-ignore -->
|
||||
{% block body %}
|
||||
|
||||
{% block title %}Login{% endblock %}
|
||||
|
||||
|
||||
{% block body %}
|
||||
<!-- component -->
|
||||
<div class="bg-gray-50 dark:bg-gray-900 h-screen pt-20">
|
||||
<section>
|
||||
|
@ -2,13 +2,21 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>{{ config.APP_NAME }}</title>
|
||||
|
||||
<title>{% block title %}{{ config.APP_NAME }}{% endblock %}</title>
|
||||
|
||||
<!-- meta -->
|
||||
<meta name="description" content="OpenLaw Flask App" />
|
||||
<meta name="author" content="Simple2B" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
|
||||
{% block meta %}{% endblock %}
|
||||
<!-- favicon -->
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/x-icon"
|
||||
href="{{ url_for('static', filename='img/logo.svg') }}"
|
||||
/>
|
||||
|
||||
<!-- styles -->
|
||||
<!-- prettier-ignore -->
|
||||
@ -35,7 +43,7 @@
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
<body class="bg-white dark:bg-gray-800">
|
||||
<body class="bg-white dark:bg-gray-800" >
|
||||
<!-- Header -->
|
||||
<!-- prettier-ignore -->
|
||||
{% include 'header.html' %}
|
||||
|
@ -18,7 +18,7 @@
|
||||
<div class="grid gap-6">
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label for="label" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white" >Label</label >
|
||||
<input type="text" name="label" id="label" 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" placeholder="Collection label" required />
|
||||
<input type="text" name="label" id="label" 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" placeholder="Section label" required />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,7 +1,9 @@
|
||||
<!-- prettier-ignore -->
|
||||
{% extends 'base.html' %}
|
||||
{% block content %}
|
||||
|
||||
{% block title %}Books{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="md:mr-64 relative overflow-x-auto shadow-md sm:rounded-lg mt-1">
|
||||
<!-- prettier-ignore -->
|
||||
<div class="p-5 flex border-b-2 border-gray-200 border-solid dark:border-gray-700 text-gray-900 dark:text-white dark:divide-gray-700">
|
||||
|
@ -12,6 +12,8 @@
|
||||
{% include 'book/delete_section_modal.html' %}
|
||||
{% endif %}
|
||||
|
||||
{% block title %}{{book.label[:32]}}{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="flex overflow-hidden">
|
||||
@ -29,7 +31,7 @@
|
||||
<div class="flex text-black dark:text-white">
|
||||
<!-- prettier-ignore -->
|
||||
<div>
|
||||
{% if not book.versions[-1].children_collections %}
|
||||
{% if not book.versions[-1].children_collections and current_user.is_authenticated %}
|
||||
<button type="button" data-modal-target="add-collection-modal" data-modal-toggle="add-collection-modal" ><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="M12 9v6m3-3H9m12 0a9 9 0 11-18 0 9 9 0 0118 0z" /> </svg> </button>
|
||||
{% endif %}
|
||||
<a href="{{ url_for("book.settings", book_id=book.id) }}" type="button" class="ml-2" >
|
||||
@ -262,7 +264,7 @@
|
||||
|
||||
<div class="gap-1 flex flex-wrap">
|
||||
{% for tag in book.tags %}
|
||||
<div class="cursor-pointer multiple-input-word bg-sky-300 hover:bg-sky-400 dark:bg-blue-600 dark:hover:bg-blue-700 dark:text-white rounded text-center py-1/2 px-2">{{tag.name}}</div>
|
||||
<a href="{{url_for('search.tag_search_interpretations',tag_name=tag.name)}}"><div class="cursor-pointer multiple-input-word bg-sky-300 hover:bg-sky-400 dark:bg-blue-600 dark:hover:bg-blue-700 dark:text-white rounded text-center py-1/2 px-2">{{tag.name}}</div></a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
<!-- prettier-ignore -->
|
||||
{% extends 'base.html' %}
|
||||
{% set selected_tab='favorite_books' %}
|
||||
{% block content %}
|
||||
|
||||
{% block title %}Favorite Books{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div
|
||||
class="md:mr-64 pt-1 relative overflow-x-auto shadow-md sm:rounded-lg mt-1 h-box w-box flex">
|
||||
{% if not current_user.is_authenticated %}
|
||||
|
@ -2,22 +2,16 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% if current_user.is_authenticated %}
|
||||
{% include 'book/delete_section_modal.html' %}
|
||||
{% include 'book/edit_section_modal.html' %}
|
||||
{% include 'book/approve_interpretation_modal.html' %}
|
||||
<!-- prettier-ignore -->
|
||||
{% set show_delete_section = True %}
|
||||
<!-- prettier-ignore -->
|
||||
{% set show_edit_section = True %}
|
||||
{% block right_sidebar %}
|
||||
{% include 'book/right_sidebar.html' %}
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
|
||||
{% block title %}{{section.label}}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include 'book/breadcrumbs_navigation.html'%}
|
||||
<div class="overflow-x-auto shadow-md sm:rounded-lg md:mr-64">
|
||||
<div class="overflow-x-auto shadow-md sm:rounded-lg">
|
||||
<!-- prettier-ignore -->
|
||||
<div class="fixed z-30 w-full top-44 pt-6 bg-white border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700">
|
||||
<!-- prettier-ignore -->
|
||||
|
@ -1,8 +1,10 @@
|
||||
<!-- prettier-ignore -->
|
||||
{% extends 'base.html' %}
|
||||
{% set selected_tab='my_contributions' %}
|
||||
{% block content %}
|
||||
|
||||
{% block title %}My Contributions{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div
|
||||
class="md:mr-64 pt-1 relative overflow-x-auto shadow-md sm:rounded-lg mt-1 h-box w-box flex">
|
||||
{% if not current_user.is_authenticated %}
|
||||
|
@ -1,6 +1,9 @@
|
||||
<!-- prettier-ignore -->
|
||||
{% extends 'base.html' %}
|
||||
{% set selected_tab='my_library' %}
|
||||
|
||||
{% block title %}My Library{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if current_user.is_authenticated %}
|
||||
@ -110,7 +113,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- prettier-ignore -->
|
||||
{% endblock %}
|
||||
<!-- prettier-ignore -->
|
||||
|
@ -4,21 +4,17 @@
|
||||
|
||||
|
||||
{% if current_user.is_authenticated %}
|
||||
{% include 'book/delete_interpretation_modal.html' %}
|
||||
{% include 'book/delete_comment_modal.html' %}
|
||||
{% include 'book/edit_comment_modal.html' %}
|
||||
{% include 'book/edit_interpretation_modal.html' %}
|
||||
{% set show_edit_interpretation = True %}
|
||||
{% set show_delete_interpretation = True %}
|
||||
{% block right_sidebar %}
|
||||
{% include 'book/right_sidebar.html' %}
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
|
||||
{% block title %}{{ section.label[:32] }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% include 'book/breadcrumbs_navigation.html'%}
|
||||
<div class="shadow-md mt-5 md:mr-64 h-auto overflow-x-hidden">
|
||||
<div class="shadow-md mt-5 h-auto overflow-x-hidden">
|
||||
<div class="ql-snow mt-20">
|
||||
<h1 class="text-l font-extrabold dark:text-white ml-4 truncate">
|
||||
{{ section.label }}
|
||||
@ -207,7 +203,7 @@
|
||||
</div>
|
||||
<div class="p-5 m-3">
|
||||
{% for child in comment.children %}
|
||||
<div class="p-5 mb-2 flex justify-between items-end bg-slate-600 rounded-lg">
|
||||
<div class="p-5 mb-2 flex justify-between items-end bg-slate-100 dark:bg-slate-600 rounded-lg">
|
||||
<div class="ql-snow">
|
||||
<div class="inline-block mb-4 ql-editor-readonly !p-0">
|
||||
{{display_tags(child.text)|safe}}
|
||||
|
@ -4,6 +4,9 @@
|
||||
{% include 'book/delete_book_modal.html' %}
|
||||
{% include 'book/access_level_modal.html' %}
|
||||
|
||||
|
||||
{% block title %}Book Settings{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Hide right_sidebar -->
|
||||
<!-- prettier-ignore -->
|
||||
|
@ -1,5 +1,8 @@
|
||||
<!-- prettier-ignore -->
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Statistics{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 md:mr-64">
|
||||
<!-- prettier-ignore -->
|
||||
|
@ -14,6 +14,7 @@
|
||||
{% include 'book/add_section_modal.html' %}
|
||||
{% endif %}
|
||||
|
||||
{% block title %}{{book.label[:32]}}{% endblock %}
|
||||
|
||||
{% block right_sidebar %}
|
||||
{% include 'book/right_sidebar.html' %}
|
||||
|
119
app/templates/search/quick_search_window.html
Normal file
119
app/templates/search/quick_search_window.html
Normal file
@ -0,0 +1,119 @@
|
||||
<!-- prettier-ignore -->
|
||||
<div id="quickSearchModal" tabindex="-1" aria-hidden="true" class="absolute w-96 top-14 left-[250px] z-50 hidden h-[calc(100%-1rem)] max-h-full">
|
||||
<div class="relative w-full max-w-2xl max-h-full">
|
||||
<!-- Modal content -->
|
||||
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
|
||||
<!-- Modal header -->
|
||||
<button type="button" class="hidden text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto items-center dark:hover:bg-gray-600 dark:hover:text-white" data-modal-hide="quickSearchModal"> <svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path> </svg>
|
||||
<span class="sr-only">Close modal</span>
|
||||
</button>
|
||||
<!-- Modal body -->
|
||||
<div class="p-1 space-y-1">
|
||||
<div id="emptyQuickSearchDiv" class="hidden h-32">
|
||||
<table>
|
||||
<thead class="text-xs text-gray-900 uppercase dark:text-gray-400">
|
||||
<tr><th scope="col" class="flex items-center justify-start px-1 py-1">
|
||||
<!-- prettier-ignore -->
|
||||
Nothing found
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
<div id="quickSearchBlock-interpretations">
|
||||
<table>
|
||||
<thead class="text-xs text-gray-900 uppercase dark:text-gray-400">
|
||||
<tr><th scope="col" class="flex items-center justify-start px-1 py-1">
|
||||
<!-- prettier-ignore -->
|
||||
Interpretations
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="interpretationsText-0">
|
||||
<th scope="row" class="flex items-center px-2 py-2 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<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 mr-2"> <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>
|
||||
<a href="" id="interpretationsText-0"></a></th>
|
||||
</tr>
|
||||
<tr class="interpretationsText-1">
|
||||
<th scope="row" class="flex items-center px-2 py-2 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<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 mr-2"> <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>
|
||||
<a href="" id="interpretationsText-1"></a></th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="quickSearchBlock-books">
|
||||
<table>
|
||||
<thead class="text-xs text-gray-900 uppercase dark:text-gray-400">
|
||||
<tr><th scope="col" class="flex items-center justify-start px-1 py-1">
|
||||
<!-- prettier-ignore -->
|
||||
Books
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="booksText-0">
|
||||
<th scope="row" class="flex items-center px-2 py-2 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<svg aria-hidden="true" class="w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M16.5 3.75V16.5L12 14.25 7.5 16.5V3.75m9 0H18A2.25 2.25 0 0120.25 6v12A2.25 2.25 0 0118 20.25H6A2.25 2.25 0 013.75 18V6A2.25 2.25 0 016 3.75h1.5m9 0h-9" /> </svg>
|
||||
<a href="" id="booksText-0"></a></th>
|
||||
</tr>
|
||||
<tr class="booksText-1">
|
||||
<th scope="row" class="flex items-center px-2 py-2 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<svg aria-hidden="true" class="w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M16.5 3.75V16.5L12 14.25 7.5 16.5V3.75m9 0H18A2.25 2.25 0 0120.25 6v12A2.25 2.25 0 0118 20.25H6A2.25 2.25 0 013.75 18V6A2.25 2.25 0 016 3.75h1.5m9 0h-9" /> </svg>
|
||||
<a href="" id="booksText-1"></a></th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="quickSearchBlock-users">
|
||||
<table>
|
||||
<thead class="text-xs text-gray-900 uppercase dark:text-gray-400">
|
||||
<tr><th scope="col" class="flex items-center justify-start px-1 py-1">
|
||||
<!-- prettier-ignore -->
|
||||
Users
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="usersText-0">
|
||||
<th scope="row" class="flex items-center px-2 py-2 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<svg aria-hidden="true" class="w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z" /> </svg>
|
||||
<a href="" id="usersText-0"></a></th>
|
||||
</tr>
|
||||
<tr class="usersText-1">
|
||||
<th scope="row" class="flex items-center px-2 py-2 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<svg aria-hidden="true" class="w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z" /> </svg>
|
||||
<a href="" id="usersText-1"></a></th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="quickSearchBlock-tags" class="hidden">
|
||||
<table>
|
||||
<thead class="text-xs text-gray-900 uppercase dark:text-gray-400">
|
||||
<tr><th scope="col" class="flex items-center justify-start px-1 py-1">
|
||||
<!-- prettier-ignore -->
|
||||
Tags
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="tagsText-0">
|
||||
<th scope="row" class="flex items-center px-2 py-2 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<svg aria-hidden="true" class="w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M9.568 3H5.25A2.25 2.25 0 003 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 005.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 009.568 3z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M6 6h.008v.008H6V6z" /> </svg>
|
||||
<a href="" id="tagsText-0"></a></th>
|
||||
</tr>
|
||||
<tr class="tagsText-1">
|
||||
<th scope="row" class="flex items-center px-2 py-2 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<svg aria-hidden="true" class="w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M9.568 3H5.25A2.25 2.25 0 003 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 005.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 009.568 3z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M6 6h.008v.008H6V6z" /> </svg>
|
||||
<a href="" id="tagsText-1"></a></th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal footer -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,5 +1,8 @@
|
||||
<!-- prettier-ignore -->
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Search results{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 md:mr-64">
|
||||
<!-- prettier-ignore -->
|
||||
@ -10,9 +13,9 @@
|
||||
<p class="hidden md:block text-sm ml-4 w-1/2 text-gray-500 text-center md:text-left dark:text-gray-400"> Showing {{count}} results </p>
|
||||
<!-- prettier-ignore -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700">
|
||||
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center text-gray-500 dark:text-gray-400">
|
||||
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center text-gray-500 dark:text-gray-400">
|
||||
<li class="mr-2">
|
||||
<a href="{{url_for('search.search_interpretations',q=query)}}" class="inline-flex p-4 border-b-2 border-transparent rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300 group" >
|
||||
<a href="{{url_for('search.search_interpretations',q=query)}}" class="inline-flex p-4 border-b-2 border-transparent rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300 group" >
|
||||
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 mr-2 text-gray-400 group-hover:text-gray-500 dark:text-gray-500 dark:group-hover:text-gray-300" fill="currentColor" viewBox="0 0 24 24" stroke-width="1.5" > <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>
|
||||
Interpretations
|
||||
</a>
|
@ -1,5 +1,8 @@
|
||||
<!-- prettier-ignore -->
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Search results{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 md:mr-64">
|
||||
<!-- prettier-ignore -->
|
||||
@ -10,9 +13,9 @@
|
||||
<p class="hidden md:block text-sm ml-4 w-1/2 text-gray-500 text-center md:text-left dark:text-gray-400"> Showing {{count}} results </p>
|
||||
<!-- prettier-ignore -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700">
|
||||
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center text-gray-500 dark:text-gray-400">
|
||||
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center text-gray-500 dark:text-gray-400">
|
||||
<li class="mr-2">
|
||||
<a class="inline-flex p-4 text-blue-600 border-b-2 border-blue-600 rounded-t-lg active dark:text-blue-500 dark:border-blue-500 group" aria-current="page">
|
||||
<a class="inline-flex p-4 text-blue-600 border-b-2 border-blue-600 rounded-t-lg active dark:text-blue-500 dark:border-blue-500 group" aria-current="page">
|
||||
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" stroke-width="1.5" class="w-5 h-5 mr-2 text-blue-600 dark:text-blue-500"> <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>
|
||||
Interpretations
|
||||
</a>
|
@ -1,5 +1,8 @@
|
||||
<!-- prettier-ignore -->
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Search results{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 md:mr-64">
|
||||
<!-- prettier-ignore -->
|
||||
@ -10,9 +13,9 @@
|
||||
<p class="hidden md:block text-sm ml-4 w-1/2 text-gray-500 text-center md:text-left dark:text-gray-400"> Showing {{count}} results </p>
|
||||
<!-- prettier-ignore -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700">
|
||||
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center text-gray-500 dark:text-gray-400">
|
||||
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center text-gray-500 dark:text-gray-400">
|
||||
<li class="mr-2">
|
||||
<a href="{{url_for('search.search_interpretations',q=query)}}" class="inline-flex p-4 border-b-2 border-transparent rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300 group" >
|
||||
<a href="{{url_for('search.search_interpretations',q=query)}}" class="inline-flex p-4 border-b-2 border-transparent rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300 group" >
|
||||
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 mr-2 text-gray-400 group-hover:text-gray-500 dark:text-gray-500 dark:group-hover:text-gray-300" fill="currentColor" viewBox="0 0 24 24" stroke-width="1.5" > <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>
|
||||
Interpretations
|
||||
</a>
|
@ -1,5 +1,8 @@
|
||||
<!-- prettier-ignore -->
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Search results{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 md:mr-64">
|
||||
<!-- prettier-ignore -->
|
||||
@ -10,9 +13,9 @@
|
||||
<p class="hidden md:block text-sm ml-4 w-1/2 text-gray-500 text-center md:text-left dark:text-gray-400"> Showing {{count}} results </p>
|
||||
<!-- prettier-ignore -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700">
|
||||
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center text-gray-500 dark:text-gray-400">
|
||||
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center text-gray-500 dark:text-gray-400">
|
||||
<li class="mr-2">
|
||||
<a href="{{url_for('search.search_interpretations',q=query)}}" class="inline-flex p-4 border-b-2 border-transparent rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300 group" >
|
||||
<a href="{{url_for('search.search_interpretations',q=query)}}" class="inline-flex p-4 border-b-2 border-transparent rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300 group" >
|
||||
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 mr-2 text-gray-400 group-hover:text-gray-500 dark:text-gray-500 dark:group-hover:text-gray-300" fill="currentColor" viewBox="0 0 24 24" stroke-width="1.5" > <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>
|
||||
Interpretations
|
||||
</a>
|
||||
@ -53,7 +56,7 @@
|
||||
<div class="pl-3">
|
||||
<div class="text-base font-semibold">{{user.username}}</div>
|
||||
<div class="font-normal text-gray-500">{{user.wallet_id}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
<td class="px-6 py-4">
|
||||
<a href="{{ url_for('user.profile',user_id=user.id) }}" class="font-medium text-blue-600 dark:text-blue-500 hover:underline">Go to {{user.username}}'s library</a>
|
@ -1,18 +1,21 @@
|
||||
<!-- prettier-ignore -->
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Tag search results{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 md:mr-64">
|
||||
<!-- prettier-ignore -->
|
||||
<h1 class="hidden md:inline font-extrabold text-lg dark:text-white ml-4 mt-5">Search results</h1>
|
||||
<h1 class="hidden md:inline font-extrabold text-lg dark:text-white ml-4 mt-5">Tag search results</h1>
|
||||
<!-- prettier-ignore -->
|
||||
<p class="hidden md:block text-sm ml-4 w-1/2 text-gray-500 text-center md:text-left dark:text-gray-400"> Search result for {{tag_name}} </p>
|
||||
<p class="hidden md:block text-sm ml-4 w-1/2 text-gray-500 text-center md:text-left dark:text-gray-400">Tag search result for {{tag_name}} </p>
|
||||
<!-- prettier-ignore -->
|
||||
<p class="hidden md:block text-sm ml-4 w-1/2 text-gray-500 text-center md:text-left dark:text-gray-400"> Showing {{count}} results </p>
|
||||
<!-- prettier-ignore -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700">
|
||||
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center text-gray-500 dark:text-gray-400">
|
||||
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center text-gray-500 dark:text-gray-400">
|
||||
<li class="mr-2">
|
||||
<a href="{{url_for('search.tag_search_interpretations',tag_name=tag_name)}}" class="inline-flex p-4 border-b-2 border-transparent rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300 group" >
|
||||
<a href="{{url_for('search.tag_search_interpretations',tag_name=tag_name)}}" class="inline-flex p-4 border-b-2 border-transparent rounded-t-lg hover:text-gray-600 hover:border-gray-300 dark:hover:text-gray-300 group" >
|
||||
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 mr-2 text-gray-400 group-hover:text-gray-500 dark:text-gray-500 dark:group-hover:text-gray-300" fill="currentColor" viewBox="0 0 24 24" stroke-width="1.5" > <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>
|
||||
Interpretations
|
||||
</a>
|
@ -1,18 +1,21 @@
|
||||
<!-- prettier-ignore -->
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}Tag search results{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 md:mr-64">
|
||||
<!-- prettier-ignore -->
|
||||
<h1 class="hidden md:inline font-extrabold text-lg dark:text-white ml-4 mt-5">Search results</h1>
|
||||
<h1 class="hidden md:inline font-extrabold text-lg dark:text-white ml-4 mt-5">Tag search results</h1>
|
||||
<!-- prettier-ignore -->
|
||||
<p class="hidden md:block text-sm ml-4 w-1/2 text-gray-500 text-center md:text-left dark:text-gray-400"> Search result for {{tag_name}} </p>
|
||||
<p class="hidden md:block text-sm ml-4 w-1/2 text-gray-500 text-center md:text-left dark:text-gray-400"> Tag search result for {{tag_name}} </p>
|
||||
<!-- prettier-ignore -->
|
||||
<p class="hidden md:block text-sm ml-4 w-1/2 text-gray-500 text-center md:text-left dark:text-gray-400"> Showing {{count}} results </p>
|
||||
<!-- prettier-ignore -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700">
|
||||
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center text-gray-500 dark:text-gray-400">
|
||||
<ul class="flex flex-wrap -mb-px text-sm font-medium text-center text-gray-500 dark:text-gray-400">
|
||||
<li class="mr-2">
|
||||
<a class="inline-flex p-4 text-blue-600 border-b-2 border-blue-600 rounded-t-lg active dark:text-blue-500 dark:border-blue-500 group" aria-current="page">
|
||||
<a class="inline-flex p-4 text-blue-600 border-b-2 border-blue-600 rounded-t-lg active dark:text-blue-500 dark:border-blue-500 group" aria-current="page">
|
||||
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" stroke-width="1.5" class="w-5 h-5 mr-2 text-blue-600 dark:text-blue-500"> <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>
|
||||
Interpretations
|
||||
</a>
|
@ -7,8 +7,9 @@
|
||||
<svg class="w-5 h-5 text-gray-500 dark:text-gray-400" aria-hidden="true" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd"></path></svg>
|
||||
</div>
|
||||
<!-- prettier-ignore -->
|
||||
<input id="mainSearchInput" required minlength="1" name="search_query" {% if search_query %}value={{ search_query }}{% endif %} type="text" class="block p-2 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg w-80 bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
|
||||
<input required minlength="1" autocomplete="off" name="search_query" {% if search_query %}value={{ search_query }}{% endif %} type="text" id="mainSearchInput" class="block p-2 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg w-80 bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" />
|
||||
</div>
|
||||
<!-- prettier-ignore -->
|
||||
<button type="button" id="global-search-button" class="md:flex px-3 py-2 text-xs text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 ml-2 font-medium rounded-lg text-center inline-flex items-center mr-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"><svg class="w-5 h-5 text-white-500" 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 ml-0 mr-1"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 15.75l-2.489-2.489m0 0a3.375 3.375 0 10-4.773-4.773 3.375 3.375 0 004.774 4.774zM21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>Search</button>
|
||||
<button type="submit" id="global-search-button" class="md:flex px-3 py-2 text-xs text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 ml-2 font-medium rounded-lg text-center inline-flex items-center mr-2 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"><svg class="w-5 h-5 text-white-500" 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 ml-0 mr-1"><path stroke-linecap="round" stroke-linejoin="round" d="M15.75 15.75l-2.489-2.489m0 0a3.375 3.375 0 10-4.773-4.773 3.375 3.375 0 004.774 4.774zM21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>Search</button>
|
||||
</div>
|
||||
{% include 'search/quick_search_window.html' %}
|
||||
|
@ -1,7 +1,9 @@
|
||||
<!-- prettier-ignore -->
|
||||
{% extends 'base.html' %}
|
||||
{% block content %}
|
||||
|
||||
{% block title %}Sections{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="md:mr-64 relative overflow-x-auto shadow-md sm:rounded-lg mt-1">
|
||||
<!-- prettier-ignore -->
|
||||
<div class="p-5 flex border-b-2 border-gray-200 border-solid dark:border-gray-700 text-gray-900 dark:text-white dark:divide-gray-700">
|
||||
|
@ -1,6 +1,9 @@
|
||||
<!-- prettier-ignore -->
|
||||
{% extends 'base.html' %}
|
||||
{% include 'user/delete_profile_modal.html' %}
|
||||
|
||||
{% block title %}Edit Profile{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- component -->
|
||||
<section>
|
||||
@ -35,19 +38,24 @@
|
||||
<div class="flex items-center mb-3">
|
||||
<div>
|
||||
{% if current_user.avatar_img %}
|
||||
<img class=" w-14 h-14 rounded-full mr-3" src="data:image/jpeg;base64,{{ current_user.avatar_img }}" alt="user avatar">
|
||||
<img class="w-14 h-14 rounded-full mr-3" src="data:image/jpeg;base64,{{ current_user.avatar_img }}" alt="user avatar">
|
||||
{% else %}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 56 56" stroke-width="1.5" stroke="currentColor" class="w-14 h-14"> <path stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z" /> </svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-14 h-14 mr-2"> <path stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z" /> </svg>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
|
||||
{{form.avatar_img(type='file', class='bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500', id="avatar_img",
|
||||
|
||||
{{form.avatar_img(type='file', class='bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500', id="avatar_img", accept=".jpg,.png,.jpeg",
|
||||
value=current_user.avatar_img if current_user.avatar_img else "")}}</div>
|
||||
</div>
|
||||
<button type="submit" class="w-full px-5 py-3 text-base font-medium text-center text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 sm:w-auto dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Save changes</button>
|
||||
</form>
|
||||
<button type="button" data-modal-target="delete_profile_modal" data-modal-toggle="delete_profile_modal" class="text-red-700 hover:text-white border border-red-700 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center mr-2 mb-2 dark:border-red-500 dark:text-red-500 dark:hover:text-white dark:hover:bg-red-600 dark:focus:ring-red-900">Delete you profile</button>
|
||||
<div class="flex">
|
||||
<form action="{{ url_for('user.delete_avatar') }}" class="mb-0" method="POST">
|
||||
<button type="submit" class="text-red-700 hover:text-white border border-red-700 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center mr-2 mb-2 dark:border-red-500 dark:text-red-500 dark:hover:text-white dark:hover:bg-red-600 dark:focus:ring-red-900">Delete Avatar</button>
|
||||
</form>
|
||||
<button type="button" data-modal-target="delete_profile_modal" data-modal-toggle="delete_profile_modal" class="text-red-700 hover:text-white border border-red-700 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center mr-2 mb-2 dark:border-red-500 dark:text-red-500 dark:hover:text-white dark:hover:bg-red-600 dark:focus:ring-red-900">Delete you profile</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,9 @@
|
||||
<!-- prettier-ignore -->
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}{{user.username.strip() + "'s profile" or user.wallet_id}}{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 md:mr-64">
|
||||
{% if user.is_deleted %}
|
||||
|
@ -1,6 +1,9 @@
|
||||
<!-- prettier-ignore -->
|
||||
{% extends 'base.html' %}
|
||||
{% include 'user/delete_profile_modal.html' %}
|
||||
|
||||
{% block title %}Reactivate Profile{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- component -->
|
||||
<section>
|
||||
|
@ -4,7 +4,6 @@ from .main import main_blueprint
|
||||
from .user import bp as user_blueprint
|
||||
from .book import bp as book_blueprint
|
||||
from .home import bp as home_blueprint
|
||||
from .section import bp as section_blueprint
|
||||
from .vote import bp as vote_blueprint
|
||||
from .approve import bp as approve_blueprint
|
||||
from .star import bp as star_blueprint
|
||||
|
@ -3,7 +3,6 @@ from flask import (
|
||||
flash,
|
||||
redirect,
|
||||
url_for,
|
||||
request,
|
||||
)
|
||||
from flask_login import login_required, current_user
|
||||
from sqlalchemy import and_, or_
|
||||
@ -29,18 +28,19 @@ from .bp import bp
|
||||
|
||||
@bp.route("/all", methods=["GET"])
|
||||
def get_all():
|
||||
q = request.args.get("q", type=str, default=None)
|
||||
books: m.Book = m.Book.query.order_by(m.Book.id)
|
||||
if q:
|
||||
books = books.filter(m.Book.label.like(f"{q}"))
|
||||
log(log.INFO, "Create query for books")
|
||||
books: m.Book = m.Book.query.filter(m.Book.is_deleted is not False).order_by(
|
||||
m.Book.id
|
||||
)
|
||||
log(log.INFO, "Create pagination for books")
|
||||
|
||||
pagination = create_pagination(total=books.count())
|
||||
log(log.INFO, "Returning data for front end")
|
||||
|
||||
return render_template(
|
||||
"book/all.html",
|
||||
books=books.paginate(page=pagination.page, per_page=pagination.per_page),
|
||||
page=pagination,
|
||||
search_query=q,
|
||||
all_books=True,
|
||||
)
|
||||
|
||||
@ -48,15 +48,22 @@ def get_all():
|
||||
@bp.route("/my_library", methods=["GET"])
|
||||
def my_library():
|
||||
if current_user.is_authenticated:
|
||||
log(log.INFO, "Create query for my_library page for books")
|
||||
|
||||
books: m.Book = m.Book.query.order_by(m.Book.id)
|
||||
books = books.filter_by(user_id=current_user.id, is_deleted=False)
|
||||
log(log.INFO, "Create pagination for books")
|
||||
|
||||
pagination = create_pagination(total=books.count())
|
||||
log(log.INFO, "Returns data for front end")
|
||||
|
||||
return render_template(
|
||||
"book/my_library.html",
|
||||
books=books.paginate(page=pagination.page, per_page=pagination.per_page),
|
||||
page=pagination,
|
||||
)
|
||||
log(log.INFO, "Returns data for front end is user is anonym")
|
||||
|
||||
return render_template(
|
||||
"book/my_library.html",
|
||||
books=[],
|
||||
@ -77,9 +84,8 @@ def create():
|
||||
root_collection = m.Collection(
|
||||
label="Root Collection", version_id=version.id, is_root=True
|
||||
).save()
|
||||
tags = form.tags.data
|
||||
if tags:
|
||||
set_book_tags(book, tags)
|
||||
tags = form.tags.data or ""
|
||||
set_book_tags(book, tags)
|
||||
|
||||
# access groups
|
||||
editor_access_group = create_editor_group(book_id=book.id)
|
||||
@ -113,9 +119,8 @@ def edit(book_id: int):
|
||||
book: m.Book = db.session.get(m.Book, book_id)
|
||||
label = form.label.data
|
||||
about = form.about.data
|
||||
tags = form.tags.data
|
||||
if tags:
|
||||
set_book_tags(book, tags)
|
||||
tags = form.tags.data or ""
|
||||
set_book_tags(book, tags)
|
||||
|
||||
book.label = label
|
||||
book.about = about
|
||||
@ -163,6 +168,8 @@ def statistic_view(book_id: int):
|
||||
@bp.route("/favorite_books", methods=["GET"])
|
||||
def favorite_books():
|
||||
if current_user.is_authenticated:
|
||||
log(log.INFO, "Creating query for books")
|
||||
|
||||
books = (
|
||||
db.session.query(
|
||||
m.Book,
|
||||
@ -178,7 +185,10 @@ def favorite_books():
|
||||
)
|
||||
|
||||
books = books.filter_by(is_deleted=False)
|
||||
log(log.INFO, "Creating pagination for books")
|
||||
|
||||
pagination = create_pagination(total=books.count())
|
||||
log(log.INFO, "Returns data for front end")
|
||||
|
||||
return render_template(
|
||||
"book/favorite_books.html",
|
||||
@ -190,34 +200,40 @@ def favorite_books():
|
||||
|
||||
@bp.route("/my_contributions", methods=["GET"])
|
||||
def my_contributions():
|
||||
interpretations = (
|
||||
db.session.query(
|
||||
m.Interpretation,
|
||||
)
|
||||
.filter(
|
||||
or_(
|
||||
and_(
|
||||
m.Interpretation.id == m.Comment.interpretation_id,
|
||||
m.Comment.user_id == current_user.id,
|
||||
m.Comment.is_deleted.is_(False),
|
||||
m.Interpretation.is_deleted.is_(False),
|
||||
),
|
||||
and_(
|
||||
m.Interpretation.user_id == current_user.id,
|
||||
m.Interpretation.is_deleted.is_(False),
|
||||
),
|
||||
if current_user.is_authenticated:
|
||||
log(log.INFO, "Creating query for interpretations")
|
||||
|
||||
interpretations = (
|
||||
db.session.query(
|
||||
m.Interpretation,
|
||||
)
|
||||
.filter(
|
||||
or_(
|
||||
and_(
|
||||
m.Interpretation.id == m.Comment.interpretation_id,
|
||||
m.Comment.user_id == current_user.id,
|
||||
m.Comment.is_deleted.is_(False),
|
||||
m.Interpretation.is_deleted.is_(False),
|
||||
),
|
||||
and_(
|
||||
m.Interpretation.user_id == current_user.id,
|
||||
m.Interpretation.is_deleted.is_(False),
|
||||
),
|
||||
)
|
||||
)
|
||||
.group_by(m.Interpretation.id)
|
||||
.order_by(m.Interpretation.created_at.desc())
|
||||
)
|
||||
.group_by(m.Interpretation.id)
|
||||
.order_by(m.Interpretation.created_at.desc())
|
||||
)
|
||||
log(log.INFO, "Creating pagination for interpretations")
|
||||
|
||||
pagination = create_pagination(total=interpretations.count())
|
||||
pagination = create_pagination(total=interpretations.count())
|
||||
log(log.INFO, "Returns data for front end")
|
||||
|
||||
return render_template(
|
||||
"book/my_contributions.html",
|
||||
interpretations=interpretations.paginate(
|
||||
page=pagination.page, per_page=pagination.per_page
|
||||
),
|
||||
page=pagination,
|
||||
)
|
||||
return render_template(
|
||||
"book/my_contributions.html",
|
||||
interpretations=interpretations.paginate(
|
||||
page=pagination.page, per_page=pagination.per_page
|
||||
),
|
||||
page=pagination,
|
||||
)
|
||||
return render_template("book/my_contributions.html", interpretations=[])
|
||||
|
@ -102,8 +102,7 @@ def create_comment(
|
||||
comment.save()
|
||||
|
||||
tags = current_app.config["TAG_REGEX"].findall(text)
|
||||
if tags:
|
||||
set_comment_tags(comment, tags)
|
||||
set_comment_tags(comment, tags)
|
||||
|
||||
flash("Success!", "success")
|
||||
return redirect(redirect_url)
|
||||
@ -198,8 +197,7 @@ def comment_edit(
|
||||
log(log.INFO, "Edit comment [%s]", comment)
|
||||
|
||||
tags = current_app.config["TAG_REGEX"].findall(text)
|
||||
if tags:
|
||||
set_comment_tags(comment, tags)
|
||||
set_comment_tags(comment, tags)
|
||||
|
||||
comment.save()
|
||||
|
||||
|
@ -143,8 +143,7 @@ def interpretation_create(
|
||||
# -------------
|
||||
|
||||
tags = current_app.config["TAG_REGEX"].findall(text)
|
||||
if tags:
|
||||
set_interpretation_tags(interpretation, tags)
|
||||
set_interpretation_tags(interpretation, tags)
|
||||
|
||||
flash("Success!", "success")
|
||||
return redirect(redirect_url)
|
||||
@ -201,8 +200,7 @@ def interpretation_edit(
|
||||
interpretation.plain_text = plain_text
|
||||
interpretation.text = text
|
||||
tags = current_app.config["TAG_REGEX"].findall(text)
|
||||
if tags:
|
||||
set_interpretation_tags(interpretation, tags)
|
||||
set_interpretation_tags(interpretation, tags)
|
||||
|
||||
log(log.INFO, "Edit interpretation [%s]", interpretation.id)
|
||||
interpretation.save()
|
||||
|
@ -4,15 +4,21 @@ from flask import (
|
||||
)
|
||||
from sqlalchemy import and_
|
||||
from app import models as m, db
|
||||
from app.logger import log
|
||||
|
||||
|
||||
bp = Blueprint("home", __name__, url_prefix="/home")
|
||||
|
||||
|
||||
@bp.route("/", methods=["GET"])
|
||||
def get_all():
|
||||
log(log.INFO, "Create query for home page for books")
|
||||
|
||||
books: m.Book = (
|
||||
m.Book.query.filter_by(is_deleted=False).order_by(m.Book.id).limit(5)
|
||||
).all()
|
||||
log(log.INFO, "Create query for home page for interpretations")
|
||||
|
||||
interpretations = (
|
||||
db.session.query(
|
||||
m.Interpretation,
|
||||
@ -34,6 +40,7 @@ def get_all():
|
||||
.limit(5)
|
||||
.all()
|
||||
)
|
||||
log(log.INFO, "Returning data to front end")
|
||||
|
||||
return render_template(
|
||||
"home/index.html",
|
||||
|
@ -1,8 +1,12 @@
|
||||
from flask import Blueprint, render_template, request
|
||||
from flask import Blueprint, render_template, request, jsonify, url_for
|
||||
from sqlalchemy import func, and_, or_
|
||||
|
||||
from app import models as m, db
|
||||
from app.controllers import create_pagination
|
||||
from app.controllers.build_qa_url_using_interpretation import (
|
||||
build_qa_url_using_interpretation,
|
||||
)
|
||||
from app.logger import log
|
||||
|
||||
|
||||
bp = Blueprint("search", __name__)
|
||||
@ -11,14 +15,18 @@ bp = Blueprint("search", __name__)
|
||||
@bp.route("/search_interpretations", methods=["GET"])
|
||||
def search_interpretations():
|
||||
q = request.args.get("q", type=str, default="").lower()
|
||||
log(log.INFO, "Starting to build query for interpretations")
|
||||
interpretations = m.Interpretation.query.order_by(m.Interpretation.id).filter(
|
||||
(func.lower(m.Interpretation.plain_text).like(f"%{q}%"))
|
||||
)
|
||||
log(log.INFO, "Get count of interpretations")
|
||||
count = interpretations.count()
|
||||
log(log.INFO, "Creating pagination")
|
||||
pagination = create_pagination(total=interpretations.count())
|
||||
log(log.INFO, "Returning data to front")
|
||||
|
||||
return render_template(
|
||||
"searchResultsInterpretations.html",
|
||||
"search/search_results_interpretations.html",
|
||||
query=q,
|
||||
interpretations=interpretations.paginate(
|
||||
page=pagination.page, per_page=pagination.per_page
|
||||
@ -30,49 +38,47 @@ def search_interpretations():
|
||||
|
||||
@bp.route("/search_books", methods=["GET"])
|
||||
def search_books():
|
||||
q = request.args.get("q", type=str, default="")
|
||||
q = request.args.get("q", type=str, default="").lower()
|
||||
log(log.INFO, "Starting to build query for books")
|
||||
|
||||
books = (
|
||||
db.session.query(m.Book)
|
||||
m.Book.query.join(m.BookVersion, m.BookVersion.book_id == m.Book.id)
|
||||
.join(m.Collection, m.BookVersion.id == m.Collection.version_id, full=True)
|
||||
.join(m.Section, m.BookVersion.id == m.Section.version_id, full=True)
|
||||
.join(m.Interpretation, m.Interpretation.section_id == m.Section.id, full=True)
|
||||
.filter(
|
||||
or_(
|
||||
and_(
|
||||
func.lower(m.Book.label).like(f"%{q}%"),
|
||||
m.Book.is_deleted == False, # noqa: E712
|
||||
),
|
||||
and_(
|
||||
func.lower(m.Collection.label).like(f"%{q}%"),
|
||||
m.Collection.is_deleted == False, # noqa: E712
|
||||
m.Collection.is_root == False, # noqa: E712
|
||||
m.BookVersion.id == m.Collection.version_id,
|
||||
m.Book.id == m.BookVersion.book_id,
|
||||
m.Book.is_deleted == False, # noqa: E712
|
||||
),
|
||||
and_(
|
||||
func.lower(m.Section.label).like(f"%{q}%"),
|
||||
m.Section.is_deleted == False, # noqa: E712
|
||||
m.BookVersion.id == m.Section.version_id,
|
||||
m.Book.id == m.BookVersion.book_id,
|
||||
m.Book.is_deleted == False, # noqa: E712
|
||||
),
|
||||
and_(
|
||||
func.lower(m.Interpretation.plain_text).like(f"%{q}%"),
|
||||
m.Interpretation.is_deleted == False, # noqa: E712
|
||||
m.Interpretation.section_id == m.Section.id,
|
||||
m.Section.is_deleted == False, # noqa: E712
|
||||
m.BookVersion.id == m.Section.version_id,
|
||||
m.Book.id == m.BookVersion.book_id,
|
||||
m.Book.is_deleted == False, # noqa: E712
|
||||
),
|
||||
),
|
||||
m.Book.is_deleted == False, # noqa: E712
|
||||
)
|
||||
.order_by(m.Book.created_at.asc())
|
||||
.group_by(m.Book.id)
|
||||
)
|
||||
|
||||
log(log.INFO, "Get count of books")
|
||||
count = books.count()
|
||||
log(log.INFO, "Creating pagination")
|
||||
pagination = create_pagination(total=books.count())
|
||||
log(log.INFO, "Returning data to front")
|
||||
|
||||
return render_template(
|
||||
"searchResultsBooks.html",
|
||||
"search/search_results_books.html",
|
||||
query=q,
|
||||
books=books.paginate(page=pagination.page, per_page=pagination.per_page),
|
||||
page=pagination,
|
||||
@ -82,7 +88,8 @@ def search_books():
|
||||
|
||||
@bp.route("/search_users", methods=["GET"])
|
||||
def search_users():
|
||||
q = request.args.get("q", type=str, default="")
|
||||
q = request.args.get("q", type=str, default="").lower()
|
||||
log(log.INFO, "Starting to build query for users")
|
||||
users = (
|
||||
m.User.query.order_by(m.User.id)
|
||||
.filter(
|
||||
@ -94,12 +101,16 @@ def search_users():
|
||||
.order_by(m.User.id.asc())
|
||||
.group_by(m.User.id)
|
||||
)
|
||||
log(log.INFO, "Get count of users")
|
||||
|
||||
count = users.count()
|
||||
log(log.INFO, "Creating pagination")
|
||||
|
||||
pagination = create_pagination(total=users.count())
|
||||
log(log.INFO, "Returning data to front")
|
||||
|
||||
return render_template(
|
||||
"searchResultsUsers.html",
|
||||
"search/search_results_users.html",
|
||||
query=q,
|
||||
users=users.paginate(page=pagination.page, per_page=pagination.per_page),
|
||||
page=pagination,
|
||||
@ -109,13 +120,20 @@ def search_users():
|
||||
|
||||
@bp.route("/search_tags", methods=["GET"])
|
||||
def search_tags():
|
||||
q = request.args.get("q", type=str, default="")
|
||||
q = request.args.get("q", type=str, default="").lower()
|
||||
log(log.INFO, "Starting to build query for tags")
|
||||
|
||||
tags = m.Tag.query.order_by(m.Tag.id).filter(func.lower(m.Tag.name).like(f"%{q}%"))
|
||||
log(log.INFO, "Get count of tags")
|
||||
|
||||
count = tags.count()
|
||||
log(log.INFO, "Creating pagination")
|
||||
|
||||
pagination = create_pagination(total=tags.count())
|
||||
log(log.INFO, "Returning data to front")
|
||||
|
||||
return render_template(
|
||||
"searchResultsTags.html",
|
||||
"search/search_results_tags.html",
|
||||
query=q,
|
||||
tags=tags.paginate(page=pagination.page, per_page=pagination.per_page),
|
||||
page=pagination,
|
||||
@ -125,7 +143,9 @@ def search_tags():
|
||||
|
||||
@bp.route("/tag_search_interpretations", methods=["GET"])
|
||||
def tag_search_interpretations():
|
||||
tag_name = request.args.get("tag_name", type=str, default="")
|
||||
tag_name = request.args.get("tag_name", type=str, default="").lower()
|
||||
log(log.INFO, "Starting to build query for interpretations")
|
||||
|
||||
interpretations = (
|
||||
db.session.query(m.Interpretation)
|
||||
.filter(
|
||||
@ -139,11 +159,13 @@ def tag_search_interpretations():
|
||||
.order_by(m.Interpretation.created_at.asc())
|
||||
.group_by(m.Interpretation.id)
|
||||
)
|
||||
log(log.INFO, "Creating pagination")
|
||||
|
||||
pagination = create_pagination(total=interpretations.count())
|
||||
log(log.INFO, "Returning data to front")
|
||||
|
||||
return render_template(
|
||||
"tagSearchResultsInterpretations.html",
|
||||
"search/tag_search_results_interpretations.html",
|
||||
tag_name=tag_name,
|
||||
interpretations=interpretations.paginate(
|
||||
page=pagination.page, per_page=pagination.per_page
|
||||
@ -155,7 +177,9 @@ def tag_search_interpretations():
|
||||
|
||||
@bp.route("/tag_search_books", methods=["GET"])
|
||||
def tag_search_books():
|
||||
tag_name = request.args.get("tag_name", type=str, default="")
|
||||
tag_name = request.args.get("tag_name", type=str, default="").lower()
|
||||
log(log.INFO, "Starting to build query for books")
|
||||
|
||||
books = (
|
||||
db.session.query(m.Book)
|
||||
.filter(
|
||||
@ -169,13 +193,89 @@ def tag_search_books():
|
||||
.order_by(m.Book.created_at.asc())
|
||||
.group_by(m.Book.id)
|
||||
)
|
||||
log(log.INFO, "Creating pagination")
|
||||
|
||||
pagination = create_pagination(total=books.count())
|
||||
log(log.INFO, "Returning data to front")
|
||||
|
||||
return render_template(
|
||||
"tagSearchResultsBooks.html",
|
||||
"search/tag_search_results_books.html",
|
||||
tag_name=tag_name,
|
||||
books=books.paginate(page=pagination.page, per_page=pagination.per_page),
|
||||
page=pagination,
|
||||
count=books.count(),
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/quick_search", methods=["GET"])
|
||||
def quick_search():
|
||||
search_query = request.args.get("search_query", type=str, default="").lower()
|
||||
log(log.INFO, "Starting to build query for interpretations")
|
||||
|
||||
interpretations = (
|
||||
m.Interpretation.query.order_by(m.Interpretation.id)
|
||||
.filter(
|
||||
(func.lower(m.Interpretation.plain_text).like(f"%{search_query}%")),
|
||||
m.Interpretation.is_deleted == False, # noqa: E712,
|
||||
)
|
||||
.limit(2)
|
||||
)
|
||||
interpretations_res = []
|
||||
for interpretation in interpretations:
|
||||
url_for_interpretation = build_qa_url_using_interpretation(interpretation)
|
||||
interpretations_res.append(
|
||||
{"label": interpretation.section.label, "url": url_for_interpretation}
|
||||
)
|
||||
log(log.INFO, "Starting to build query for books")
|
||||
|
||||
books = (
|
||||
m.Book.query.order_by(m.Book.id)
|
||||
.filter(
|
||||
(func.lower(m.Book.label).like(f"%{search_query}%")),
|
||||
m.Book.is_deleted == False, # noqa: E712,
|
||||
)
|
||||
.limit(2)
|
||||
)
|
||||
books_res = []
|
||||
for book in books:
|
||||
url_for_book = url_for("book.collection_view", book_id=book.id)
|
||||
books_res.append({"label": book.label, "url": url_for_book})
|
||||
log(log.INFO, "Starting to build query for users")
|
||||
|
||||
users = (
|
||||
m.User.query.order_by(m.User.id)
|
||||
.filter(
|
||||
or_(
|
||||
func.lower(m.User.username).like(f"%{search_query}%"),
|
||||
func.lower(m.User.wallet_id).like(f"%{search_query}%"),
|
||||
)
|
||||
)
|
||||
.order_by(m.User.id.asc())
|
||||
.group_by(m.User.id)
|
||||
.limit(2)
|
||||
)
|
||||
users_res = []
|
||||
for user in users:
|
||||
url_for_user = url_for("user.profile", user_id=user.id)
|
||||
users_res.append({"label": user.username, "url": url_for_user})
|
||||
log(log.INFO, "Starting to build query for tags")
|
||||
|
||||
tags = (
|
||||
m.Tag.query.order_by(m.Tag.id)
|
||||
.filter(func.lower(m.Tag.name).like(f"%{search_query}%"))
|
||||
.limit(2)
|
||||
)
|
||||
tags_res = []
|
||||
for tag in tags:
|
||||
url_for_tag = url_for("search.tag_search_interpretations", tag_name=tag.name)
|
||||
tags_res.append({"label": tag.name, "url": url_for_tag})
|
||||
log(log.INFO, "Returning data to front")
|
||||
|
||||
return jsonify(
|
||||
{
|
||||
"interpretations": interpretations_res,
|
||||
"books": books_res,
|
||||
"users": users_res,
|
||||
"tags": tags_res,
|
||||
}
|
||||
)
|
||||
|
@ -1,29 +0,0 @@
|
||||
from flask import (
|
||||
Blueprint,
|
||||
render_template,
|
||||
request,
|
||||
)
|
||||
|
||||
from app.controllers import create_pagination
|
||||
from app import models as m
|
||||
|
||||
|
||||
bp = Blueprint("section", __name__, url_prefix="/section")
|
||||
|
||||
|
||||
@bp.route("/all", methods=["GET"])
|
||||
def get_all():
|
||||
q = request.args.get("q", type=str, default=None)
|
||||
section: m.Section = m.Section.query.order_by(m.Section.id)
|
||||
if q:
|
||||
section = section.filter(m.Section.label.like(f"{q}"))
|
||||
|
||||
pagination = create_pagination(total=section.count())
|
||||
|
||||
return render_template(
|
||||
"section/all.html",
|
||||
sections=section.paginate(page=pagination.page, per_page=pagination.per_page),
|
||||
page=pagination,
|
||||
search_query=q,
|
||||
all_books=True,
|
||||
)
|
@ -1,5 +1,3 @@
|
||||
import base64
|
||||
|
||||
from flask import Blueprint, render_template, request, flash, redirect, url_for, jsonify
|
||||
from flask_login import login_required, current_user, logout_user
|
||||
from app.controllers import create_pagination
|
||||
@ -40,9 +38,9 @@ def edit_profile():
|
||||
user: m.User = current_user
|
||||
user.username = form.name.data
|
||||
if form.avatar_img.data:
|
||||
img_data = form.avatar_img.data.read()
|
||||
img_data = base64.b64encode(img_data)
|
||||
current_user.avatar_img = img_data.decode("utf-8")
|
||||
current_user.avatar_img = (
|
||||
form.avatar_img.data
|
||||
) # form.avatar_img.data is changed in form validator
|
||||
user.is_activated = True
|
||||
user.save()
|
||||
return redirect(url_for("main.index"))
|
||||
@ -58,6 +56,17 @@ def edit_profile():
|
||||
return render_template("user/edit_profile.html", form=form)
|
||||
|
||||
|
||||
@bp.route("/delete_avatar", methods=["POST"])
|
||||
@login_required
|
||||
def delete_avatar():
|
||||
user: m.User = current_user
|
||||
current_user.avatar_img = None
|
||||
log(log.ERROR, "Delete user [%s] avatar", user)
|
||||
current_user.save()
|
||||
|
||||
return redirect(url_for("user.edit_profile"))
|
||||
|
||||
|
||||
@bp.route("/<int:user_id>/profile")
|
||||
def profile(user_id: int):
|
||||
user: m.User = db.session.get(m.User, user_id)
|
||||
@ -72,22 +81,6 @@ def profile(user_id: int):
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/create", methods=["POST"])
|
||||
@login_required
|
||||
def create():
|
||||
form = f.NewUserForm()
|
||||
if form.validate_on_submit():
|
||||
user = m.User(
|
||||
username=form.username.data,
|
||||
password=form.password.data,
|
||||
activated=form.activated.data,
|
||||
)
|
||||
log(log.INFO, "Form submitted. User: [%s]", user)
|
||||
flash("User added!", "success")
|
||||
user.save()
|
||||
return redirect(url_for("user.get_all"))
|
||||
|
||||
|
||||
@bp.route("/profile_delete", methods=["POST"])
|
||||
@login_required
|
||||
def profile_delete():
|
||||
|
@ -14,17 +14,17 @@ config = context.config
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
fileConfig(config.config_file_name)
|
||||
logger = logging.getLogger('alembic.env')
|
||||
logger = logging.getLogger("alembic.env")
|
||||
|
||||
# add your model's MetaData object here
|
||||
# for 'autogenerate' support
|
||||
# from myapp import mymodel
|
||||
# target_metadata = mymodel.Base.metadata
|
||||
config.set_main_option(
|
||||
'sqlalchemy.url',
|
||||
str(current_app.extensions['migrate'].db.get_engine().url).replace(
|
||||
'%', '%%'))
|
||||
target_metadata = current_app.extensions['migrate'].db.metadata
|
||||
"sqlalchemy.url",
|
||||
str(current_app.extensions["migrate"].db.get_engine().url).replace("%", "%%"),
|
||||
)
|
||||
target_metadata = current_app.extensions["migrate"].db.metadata
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
@ -45,9 +45,7 @@ def run_migrations_offline():
|
||||
|
||||
"""
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
context.configure(
|
||||
url=url, target_metadata=target_metadata, literal_binds=True
|
||||
)
|
||||
context.configure(url=url, target_metadata=target_metadata, literal_binds=True)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
@ -65,20 +63,20 @@ def run_migrations_online():
|
||||
# when there are no changes to the schema
|
||||
# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
|
||||
def process_revision_directives(context, revision, directives):
|
||||
if getattr(config.cmd_opts, 'autogenerate', False):
|
||||
if getattr(config.cmd_opts, "autogenerate", False):
|
||||
script = directives[0]
|
||||
if script.upgrade_ops.is_empty():
|
||||
directives[:] = []
|
||||
logger.info('No changes in schema detected.')
|
||||
logger.info("No changes in schema detected.")
|
||||
|
||||
connectable = current_app.extensions['migrate'].db.get_engine()
|
||||
connectable = current_app.extensions["migrate"].db.get_engine()
|
||||
|
||||
with connectable.connect() as connection:
|
||||
context.configure(
|
||||
connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
process_revision_directives=process_revision_directives,
|
||||
**current_app.extensions['migrate'].configure_args
|
||||
**current_app.extensions["migrate"].configure_args
|
||||
)
|
||||
|
||||
with context.begin_transaction():
|
||||
|
@ -10,23 +10,23 @@ import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '1dfa1f2c208f'
|
||||
down_revision = '2ec60080de3b'
|
||||
revision = "1dfa1f2c208f"
|
||||
down_revision = "2ec60080de3b"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('comments', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('edited', sa.Boolean(), nullable=True))
|
||||
with op.batch_alter_table("comments", schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column("edited", sa.Boolean(), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('comments', schema=None) as batch_op:
|
||||
batch_op.drop_column('edited')
|
||||
with op.batch_alter_table("comments", schema=None) as batch_op:
|
||||
batch_op.drop_column("edited")
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
@ -10,29 +10,33 @@ import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '2ec60080de3b'
|
||||
down_revision = '4ce4bacc7c06'
|
||||
revision = "2ec60080de3b"
|
||||
down_revision = "4ce4bacc7c06"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||
batch_op.alter_column('wallet_id',
|
||||
existing_type=sa.VARCHAR(length=256),
|
||||
type_=sa.String(length=64),
|
||||
existing_nullable=True)
|
||||
with op.batch_alter_table("users", schema=None) as batch_op:
|
||||
batch_op.alter_column(
|
||||
"wallet_id",
|
||||
existing_type=sa.VARCHAR(length=256),
|
||||
type_=sa.String(length=64),
|
||||
existing_nullable=True,
|
||||
)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||
batch_op.alter_column('wallet_id',
|
||||
existing_type=sa.String(length=64),
|
||||
type_=sa.VARCHAR(length=256),
|
||||
existing_nullable=True)
|
||||
with op.batch_alter_table("users", schema=None) as batch_op:
|
||||
batch_op.alter_column(
|
||||
"wallet_id",
|
||||
existing_type=sa.String(length=64),
|
||||
type_=sa.VARCHAR(length=256),
|
||||
existing_nullable=True,
|
||||
)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
@ -10,29 +10,29 @@ import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '377fc0b7e4bb'
|
||||
down_revision = 'a1345b416f81'
|
||||
revision = "377fc0b7e4bb"
|
||||
down_revision = "a1345b416f81"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('is_activated', sa.Boolean(), nullable=True))
|
||||
batch_op.alter_column('username',
|
||||
existing_type=sa.VARCHAR(length=60),
|
||||
nullable=True)
|
||||
with op.batch_alter_table("users", schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column("is_activated", sa.Boolean(), nullable=True))
|
||||
batch_op.alter_column(
|
||||
"username", existing_type=sa.VARCHAR(length=60), nullable=True
|
||||
)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||
batch_op.alter_column('username',
|
||||
existing_type=sa.VARCHAR(length=60),
|
||||
nullable=False)
|
||||
batch_op.drop_column('is_activated')
|
||||
with op.batch_alter_table("users", schema=None) as batch_op:
|
||||
batch_op.alter_column(
|
||||
"username", existing_type=sa.VARCHAR(length=60), nullable=False
|
||||
)
|
||||
batch_op.drop_column("is_activated")
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
@ -10,45 +10,57 @@ import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4ce4bacc7c06'
|
||||
down_revision = '377fc0b7e4bb'
|
||||
revision = "4ce4bacc7c06"
|
||||
down_revision = "377fc0b7e4bb"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||
batch_op.alter_column('username',
|
||||
existing_type=sa.VARCHAR(length=60),
|
||||
type_=sa.String(length=64),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('password_hash',
|
||||
existing_type=sa.VARCHAR(length=255),
|
||||
type_=sa.String(length=256),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('wallet_id',
|
||||
existing_type=sa.VARCHAR(length=255),
|
||||
type_=sa.String(length=256),
|
||||
existing_nullable=True)
|
||||
with op.batch_alter_table("users", schema=None) as batch_op:
|
||||
batch_op.alter_column(
|
||||
"username",
|
||||
existing_type=sa.VARCHAR(length=60),
|
||||
type_=sa.String(length=64),
|
||||
existing_nullable=True,
|
||||
)
|
||||
batch_op.alter_column(
|
||||
"password_hash",
|
||||
existing_type=sa.VARCHAR(length=255),
|
||||
type_=sa.String(length=256),
|
||||
existing_nullable=True,
|
||||
)
|
||||
batch_op.alter_column(
|
||||
"wallet_id",
|
||||
existing_type=sa.VARCHAR(length=255),
|
||||
type_=sa.String(length=256),
|
||||
existing_nullable=True,
|
||||
)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||
batch_op.alter_column('wallet_id',
|
||||
existing_type=sa.String(length=256),
|
||||
type_=sa.VARCHAR(length=255),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('password_hash',
|
||||
existing_type=sa.String(length=256),
|
||||
type_=sa.VARCHAR(length=255),
|
||||
existing_nullable=True)
|
||||
batch_op.alter_column('username',
|
||||
existing_type=sa.String(length=64),
|
||||
type_=sa.VARCHAR(length=60),
|
||||
existing_nullable=True)
|
||||
with op.batch_alter_table("users", schema=None) as batch_op:
|
||||
batch_op.alter_column(
|
||||
"wallet_id",
|
||||
existing_type=sa.String(length=256),
|
||||
type_=sa.VARCHAR(length=255),
|
||||
existing_nullable=True,
|
||||
)
|
||||
batch_op.alter_column(
|
||||
"password_hash",
|
||||
existing_type=sa.String(length=256),
|
||||
type_=sa.VARCHAR(length=255),
|
||||
existing_nullable=True,
|
||||
)
|
||||
batch_op.alter_column(
|
||||
"username",
|
||||
existing_type=sa.String(length=64),
|
||||
type_=sa.VARCHAR(length=60),
|
||||
existing_nullable=True,
|
||||
)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
@ -10,31 +10,38 @@ import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '5df1fabbee7d'
|
||||
down_revision = '1dfa1f2c208f'
|
||||
revision = "5df1fabbee7d"
|
||||
down_revision = "1dfa1f2c208f"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('comments', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('approved', sa.Boolean(), nullable=True))
|
||||
batch_op.drop_column('included_with_interpretation')
|
||||
with op.batch_alter_table("comments", schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column("approved", sa.Boolean(), nullable=True))
|
||||
batch_op.drop_column("included_with_interpretation")
|
||||
|
||||
with op.batch_alter_table('interpretations', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('approved', sa.Boolean(), nullable=True))
|
||||
with op.batch_alter_table("interpretations", schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column("approved", sa.Boolean(), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('interpretations', schema=None) as batch_op:
|
||||
batch_op.drop_column('approved')
|
||||
with op.batch_alter_table("interpretations", schema=None) as batch_op:
|
||||
batch_op.drop_column("approved")
|
||||
|
||||
with op.batch_alter_table('comments', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('included_with_interpretation', sa.BOOLEAN(), autoincrement=False, nullable=True))
|
||||
batch_op.drop_column('approved')
|
||||
with op.batch_alter_table("comments", schema=None) as batch_op:
|
||||
batch_op.add_column(
|
||||
sa.Column(
|
||||
"included_with_interpretation",
|
||||
sa.BOOLEAN(),
|
||||
autoincrement=False,
|
||||
nullable=True,
|
||||
)
|
||||
)
|
||||
batch_op.drop_column("approved")
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
@ -10,28 +10,35 @@ import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '7baa732e01c6'
|
||||
down_revision = '0961578f302a'
|
||||
revision = "7baa732e01c6"
|
||||
down_revision = "0961578f302a"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('section_tags',
|
||||
sa.Column('tag_id', sa.Integer(), nullable=True),
|
||||
sa.Column('section_id', sa.Integer(), nullable=True),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('is_deleted', sa.Boolean(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['section_id'], ['sections.id'], ),
|
||||
sa.ForeignKeyConstraint(['tag_id'], ['tags.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
op.create_table(
|
||||
"section_tags",
|
||||
sa.Column("tag_id", sa.Integer(), nullable=True),
|
||||
sa.Column("section_id", sa.Integer(), nullable=True),
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=True),
|
||||
sa.Column("is_deleted", sa.Boolean(), nullable=True),
|
||||
sa.ForeignKeyConstraint(
|
||||
["section_id"],
|
||||
["sections.id"],
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["tag_id"],
|
||||
["tags.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('section_tags')
|
||||
op.drop_table("section_tags")
|
||||
# ### end Alembic commands ###
|
||||
|
@ -10,29 +10,35 @@ import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '883298018384'
|
||||
down_revision = '5df1fabbee7d'
|
||||
revision = "883298018384"
|
||||
down_revision = "5df1fabbee7d"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('interpretations', schema=None) as batch_op:
|
||||
batch_op.drop_column('label')
|
||||
with op.batch_alter_table("interpretations", schema=None) as batch_op:
|
||||
batch_op.drop_column("label")
|
||||
|
||||
with op.batch_alter_table('sections', schema=None) as batch_op:
|
||||
batch_op.drop_column('about')
|
||||
with op.batch_alter_table("sections", schema=None) as batch_op:
|
||||
batch_op.drop_column("about")
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('sections', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('about', sa.TEXT(), autoincrement=False, nullable=True))
|
||||
with op.batch_alter_table("sections", schema=None) as batch_op:
|
||||
batch_op.add_column(
|
||||
sa.Column("about", sa.TEXT(), autoincrement=False, nullable=True)
|
||||
)
|
||||
|
||||
with op.batch_alter_table('interpretations', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('label', sa.VARCHAR(length=256), autoincrement=False, nullable=False))
|
||||
with op.batch_alter_table("interpretations", schema=None) as batch_op:
|
||||
batch_op.add_column(
|
||||
sa.Column(
|
||||
"label", sa.VARCHAR(length=256), autoincrement=False, nullable=False
|
||||
)
|
||||
)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
@ -10,23 +10,23 @@ import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'a1345b416f81'
|
||||
down_revision = '067a10a531d7'
|
||||
revision = "a1345b416f81"
|
||||
down_revision = "067a10a531d7"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('avatar_img', sa.Text(), nullable=True))
|
||||
with op.batch_alter_table("users", schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column("avatar_img", sa.Text(), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('users', schema=None) as batch_op:
|
||||
batch_op.drop_column('avatar_img')
|
||||
with op.batch_alter_table("users", schema=None) as batch_op:
|
||||
batch_op.drop_column("avatar_img")
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
@ -13,8 +13,10 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@types/lodash.debounce": "^4.0.7",
|
||||
"ethers": "5.5.3",
|
||||
"flowbite": "^1.6.4",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"siwe": "1.0.0",
|
||||
"tailwindcss": "^3.2.7"
|
||||
},
|
||||
|
@ -46,6 +46,10 @@ export function addSection() {
|
||||
`${collectionId}/${subCollectionId}/create_section`,
|
||||
);
|
||||
}
|
||||
if (newActionPath.includes('/0')) {
|
||||
console.log('ALERT');
|
||||
return;
|
||||
}
|
||||
|
||||
addSectionForm.setAttribute('action', `${newActionPath}`);
|
||||
sectionModal.show();
|
||||
|
@ -24,6 +24,7 @@ import {scroll} from './scroll';
|
||||
import {initCheckBoxTree} from './checkBoxTree';
|
||||
import {initPermissions} from './permissions';
|
||||
import {copyLink} from './copyLink';
|
||||
import {quickSearch} from './quickSearch';
|
||||
import {flash} from './flash';
|
||||
import {slashSearch} from './slashSearch';
|
||||
|
||||
@ -53,5 +54,6 @@ scroll();
|
||||
initCheckBoxTree();
|
||||
initPermissions();
|
||||
copyLink();
|
||||
quickSearch();
|
||||
flash();
|
||||
slashSearch();
|
||||
|
@ -63,7 +63,7 @@ const multipleInputJs = () => {
|
||||
tagsToSubmitInput.value = addedWords.join();
|
||||
|
||||
multipleInput.addEventListener('input', () => {
|
||||
let inputValue = multipleInput.value.trim();
|
||||
let inputValue = multipleInput.value.trim().toLowerCase();
|
||||
if (inputValue.length > 32) {
|
||||
multipleInput.value = inputValue.slice(0, 32);
|
||||
return;
|
||||
|
92
src/quickSearch.ts
Normal file
92
src/quickSearch.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import {Modal} from 'flowbite';
|
||||
import type {ModalOptions, ModalInterface} from 'flowbite';
|
||||
import debounce = require('lodash.debounce');
|
||||
|
||||
const currentSearchInput: HTMLInputElement =
|
||||
document.querySelector('#mainSearchInput');
|
||||
const searchDiv: HTMLElement = document.querySelector('#quickSearchModal');
|
||||
|
||||
const modalOptions: ModalOptions = {
|
||||
closable: true,
|
||||
backdrop: 'static',
|
||||
onHide: () => {},
|
||||
onShow: () => {},
|
||||
onToggle: () => {},
|
||||
};
|
||||
|
||||
const quickSearchModal: ModalInterface = new Modal(searchDiv, modalOptions);
|
||||
|
||||
export function quickSearch() {
|
||||
if (currentSearchInput && searchDiv) {
|
||||
currentSearchInput.addEventListener('input', debounce(onInputChange, 500));
|
||||
currentSearchInput.addEventListener('keypress', async e => {
|
||||
if (e.key === 'Enter') {
|
||||
const urlParams = new URLSearchParams({
|
||||
q: currentSearchInput.value.toLowerCase(),
|
||||
});
|
||||
const res = await fetch('/search_interpretations?' + urlParams);
|
||||
if (res.status === 200) {
|
||||
window.location.replace(res.url);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const onInputChange = async (e: any) => {
|
||||
e.preventDefault();
|
||||
if (currentSearchInput.value.length > 0) {
|
||||
const urlParams = new URLSearchParams({
|
||||
search_query: currentSearchInput.value.toLowerCase(),
|
||||
});
|
||||
const res = await fetch('/quick_search?' + urlParams);
|
||||
const json = await res.json();
|
||||
if (res.status === 200) {
|
||||
let emptyEntityArr = [];
|
||||
|
||||
for (const entity in json) {
|
||||
// iterate over json from back end
|
||||
const el: HTMLDivElement = document.querySelector(
|
||||
`#quickSearchBlock-${entity}`,
|
||||
);
|
||||
const secondUnusedLink = document.querySelector(`.${entity}Text-1`);
|
||||
const emptySearchDiv = document.querySelector('#emptyQuickSearchDiv');
|
||||
if (secondUnusedLink) {
|
||||
secondUnusedLink.classList.remove('hidden');
|
||||
}
|
||||
if (json[entity].length < 1) {
|
||||
emptyEntityArr.push(entity);
|
||||
if (el) {
|
||||
el.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
if (json[entity].length == 1) {
|
||||
if (secondUnusedLink) {
|
||||
secondUnusedLink.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
if (emptyEntityArr.length === 4) {
|
||||
emptySearchDiv.classList.remove('hidden');
|
||||
}
|
||||
|
||||
for (const obj in json[entity]) {
|
||||
// iterate over every entity in json
|
||||
el.classList.remove('hidden');
|
||||
emptySearchDiv.classList.add('hidden');
|
||||
const link = document.querySelector(`#${entity}Text-${obj}`);
|
||||
// taking needed html element for markup
|
||||
if (link) {
|
||||
// setting needed values to element
|
||||
link.textContent = json[entity][obj].label;
|
||||
link.setAttribute('href', json[entity][obj].url);
|
||||
}
|
||||
}
|
||||
}
|
||||
quickSearchModal.show();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
@ -23,7 +23,7 @@ def test_create_tags_on_book_create_and_edit(client: FlaskClient):
|
||||
book = m.Book.query.filter_by(label=BOOK_NAME).first()
|
||||
assert book.tags
|
||||
|
||||
splitted_tags = [tag.title() for tag in tags.split(",")]
|
||||
splitted_tags = [tag.lower() for tag in tags.split(",")]
|
||||
assert len(book.tags) == 3
|
||||
for tag in book.tags:
|
||||
tag: m.Tag
|
||||
@ -42,7 +42,7 @@ def test_create_tags_on_book_create_and_edit(client: FlaskClient):
|
||||
|
||||
book: m.Book = m.Book.query.first()
|
||||
|
||||
splitted_tags = [tag.title() for tag in tags.split(",")]
|
||||
splitted_tags = [tag.lower() for tag in tags.split(",")]
|
||||
assert len(book.tags) == 3
|
||||
for tag in book.tags:
|
||||
tag: m.Tag
|
||||
|
@ -133,6 +133,15 @@ def test_profile(client):
|
||||
assert res.status_code == 200
|
||||
assert str.encode(new_name) in res.data
|
||||
|
||||
# delete_avatar
|
||||
assert user.avatar_img
|
||||
res = client.post(
|
||||
"/user/delete_avatar",
|
||||
follow_redirects=True,
|
||||
)
|
||||
assert res
|
||||
assert not user.avatar_img
|
||||
|
||||
# delete_profile
|
||||
res = client.post(
|
||||
"/user/profile_delete",
|
||||
|
17
yarn.lock
17
yarn.lock
@ -1113,6 +1113,18 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
|
||||
integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
|
||||
|
||||
"@types/lodash.debounce@^4.0.7":
|
||||
version "4.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash.debounce/-/lodash.debounce-4.0.7.tgz#0285879defb7cdb156ae633cecd62d5680eded9f"
|
||||
integrity sha512-X1T4wMZ+gT000M2/91SYj0d/7JfeNZ9PeeOldSNoE/lunLeQXKvkmIumI29IaKMotU/ln/McOIvgzZcQ/3TrSA==
|
||||
dependencies:
|
||||
"@types/lodash" "*"
|
||||
|
||||
"@types/lodash@*":
|
||||
version "4.14.194"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.194.tgz#b71eb6f7a0ff11bff59fc987134a093029258a76"
|
||||
integrity sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==
|
||||
|
||||
"@types/mime@*":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10"
|
||||
@ -2606,6 +2618,11 @@ locate-path@^5.0.0:
|
||||
dependencies:
|
||||
p-locate "^4.1.0"
|
||||
|
||||
lodash.debounce@^4.0.8:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
||||
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
|
||||
|
||||
lru-cache@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
|
||||
|
Loading…
x
Reference in New Issue
Block a user