Merge pull request #68 from Simple2B/kostia/feature/quicksearch

Kostia/feature/quicksearch
This commit is contained in:
Костя Столярский 2023-05-25 17:46:20 +03:00 committed by GitHub
commit c124af9dc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 286 additions and 5 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,109 @@
<!-- 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="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 -->
<svg aria-hidden="true" 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" 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>
Tags
</th>
</tr>
</thead>
<tbody>
<tr class="tagsText-0">
<th scope="row" class="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>

View File

@ -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 'quickSearchWindow.html' %}

View File

@ -1,8 +1,11 @@
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,
)
bp = Blueprint("search", __name__)
@ -179,3 +182,64 @@ def tag_search_books():
page=pagination,
count=books.count(),
)
@bp.route("/quick_search", methods=["GET"])
def quick_search():
search_query = request.args.get("search_query", type=str, default="")
interpretations = (
m.Interpretation.query.order_by(m.Interpretation.id)
.filter((func.lower(m.Interpretation.plain_text).like(f"%{search_query}%")))
.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}
)
books = (
m.Book.query.order_by(m.Book.id)
.filter((func.lower(m.Book.label).like(f"%{search_query}%")))
.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})
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})
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})
return jsonify(
{
"interpretations": interpretations_res,
"books": books_res,
"users": users_res,
"tags": tags_res,
}
)

View File

@ -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"
},

View File

@ -22,6 +22,7 @@ import {initQuillReadOnly} from './initQuillReadOnly';
import {initGoBack} from './tabGoBackBtn';
import {scroll} from './scroll';
import {copyLink} from './copyLink';
import {quickSearch} from './quickSearch';
import {flash} from './flash';
import {slashSearch} from './slashSearch';
@ -49,5 +50,6 @@ renameSubCollection();
initGoBack();
scroll();
copyLink();
quickSearch();
flash();
slashSearch();

86
src/quickSearch.ts Normal file
View File

@ -0,0 +1,86 @@
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,
onHide: () => {},
onShow: () => {},
onToggle: () => {},
};
const quickSearchModal: ModalInterface = new Modal(searchDiv, modalOptions);
export function quickSearch() {
if (currentSearchInput && searchDiv) {
currentSearchInput.addEventListener('input', debounce(onInputChange, 500));
currentSearchInput.addEventListener('blur', () => {
quickSearchModal.hide();
});
currentSearchInput.addEventListener('keypress', async e => {
if (e.key === 'Enter') {
const urlParams = new URLSearchParams({
q: currentSearchInput.value,
});
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,
});
const res = await fetch('/quick_search?' + urlParams);
const json = await res.json();
if (res.status === 200) {
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`);
if (secondUnusedLink) {
secondUnusedLink.classList.remove('hidden');
}
if (json[entity].length < 1) {
if (el) {
el.classList.add('hidden');
}
}
if (json[entity].length == 1) {
if (secondUnusedLink) {
secondUnusedLink.classList.add('hidden');
}
}
for (const obj in json[entity]) {
// iterate over every entity in json
el.classList.remove('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;
}
}
};

View File

@ -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"