forked from enviPath/enviPy
This PR moves all the collection pages into the new UI in a rough push. I did not put the same amount of care into these as into search, index, and predict. ## Major changes - All modals are now migrated to a state based alpine.js implementation. - jQuery is no longer present in the base layout; ajax is replace by native fetch api - most of the pps.js is now obsolte (as I understand it; the code is not referenced any more @jebus please double check) - in-memory pagination for large result lists (set to 50; we can make that configurable later; performance degrades at around 1k) stukk a bit rough tracked in #235 ## Minor things - Sarch and index also use alpine now - The loading spinner is now CSS animated (not sure if it currently gets correctly called) ## Not done - Ihave not even cheked the admin pages. Not sure If these need migrations - The temporary migration pages still use the old template. Not sure what is supposed to happen with those? @jebus ## What I did to test - opend all pages in browse, and user ; plus all pages reachable from there. - Interacted and tested the functionality of each modal superfically with exception of the API key modal (no functional test). --- This PR is massive sorry for that; just did not want to push half-brokenn state. @jebus @liambrydon I would be glad if you could click around and try to break it :) Finally closes #133 Co-authored-by: Tim Lorsbach <tim@lorsba.ch> Reviewed-on: enviPath/enviPy#236 Co-authored-by: Tobias O <tobias.olenyi@envipath.com> Co-committed-by: Tobias O <tobias.olenyi@envipath.com>
503 lines
17 KiB
HTML
503 lines
17 KiB
HTML
{% load static %}
|
|
<dialog
|
|
id="search_modal"
|
|
class="modal @max-sm:modal-top justify-center"
|
|
x-data="searchModal()"
|
|
@close="reset()"
|
|
>
|
|
<div class="modal-box h-full w-lvw p-1 sm:h-8/12 sm:w-[85vw] sm:max-w-5xl">
|
|
<!-- Search Input and Mode Selector -->
|
|
<div class="form-control mb-4 w-full shrink-0">
|
|
<div class="join m-0 w-full items-center p-3">
|
|
<label class="input join-item input-ghost grow">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
class="lucide lucide-search-icon lucide-search"
|
|
>
|
|
<path d="m21 21-4.34-4.34" />
|
|
<circle cx="11" cy="11" r="8" />
|
|
</svg>
|
|
<input
|
|
type="text"
|
|
autofocus
|
|
x-ref="searchbar"
|
|
x-model="query"
|
|
@keydown.enter="performSearch('{{ SERVER_BASE }}')"
|
|
placeholder="Benfuracarb"
|
|
class="grow"
|
|
aria-label="Search"
|
|
/>
|
|
</label>
|
|
|
|
<!-- Mode Dropdown -->
|
|
<div>
|
|
<button
|
|
type="button"
|
|
tabindex="0"
|
|
popovertarget="search_dropdown_menu"
|
|
style="anchor-name: --1"
|
|
class="btn join-item btn-ghost"
|
|
>
|
|
<span x-text="searchModeLabel"></span>
|
|
<svg
|
|
class="ml-1 h-4 w-4"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M19 9l-7 7-7-7"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
<ul
|
|
tabindex="0"
|
|
class="dropdown dropdown-end menu bg-base-200 rounded-box w-64 p-2 shadow-lg"
|
|
popover
|
|
x-ref="modeDropdown"
|
|
id="search_dropdown_menu"
|
|
style="position-anchor: --anchor-2"
|
|
>
|
|
<li class="menu-title">Text</li>
|
|
<li>
|
|
<a
|
|
@click.prevent="setSearchMode('text', 'Text')"
|
|
class="tooltip tooltip-left"
|
|
data-tip="Search on object names and descriptions"
|
|
>
|
|
Text
|
|
</a>
|
|
</li>
|
|
<li class="menu-title">SMILES</li>
|
|
<li>
|
|
<a
|
|
@click.prevent="setSearchMode('smiles_default', 'Default')"
|
|
class="tooltip tooltip-left"
|
|
data-tip="Ignores stereochemistry and charge"
|
|
>
|
|
Default
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a
|
|
@click.prevent="setSearchMode('smiles_canonical', 'Canonical')"
|
|
class="tooltip tooltip-left"
|
|
data-tip="Ignores stereochemistry, preserves charge"
|
|
>
|
|
Canonical
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a
|
|
@click.prevent="setSearchMode('smiles_exact', 'Exact')"
|
|
class="tooltip tooltip-left"
|
|
data-tip="Exact match for stereochemistry and charge"
|
|
>
|
|
Exact
|
|
</a>
|
|
</li>
|
|
<li class="menu-title">InChI</li>
|
|
<li>
|
|
<a
|
|
@click.prevent="setSearchMode('inchikey', 'InChIKey')"
|
|
class="tooltip tooltip-left"
|
|
data-tip="Search by InChIKey"
|
|
>
|
|
InChIKey
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<button
|
|
type="button"
|
|
@click="performSearch('{{ SERVER_BASE }}')"
|
|
class="btn btn-xs btn-ghost join-item"
|
|
>
|
|
<kbd class="kbd kbd-sm text-base-content/50 p-1">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
class="lucide lucide-corner-down-left-icon lucide-corner-down-left"
|
|
>
|
|
<path d="M20 4v7a4 4 0 0 1-4 4H4" />
|
|
<path d="m9 10-5 5 5 5" />
|
|
</svg>
|
|
</kbd>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Package Selector with Pills -->
|
|
<div class="form-control mb-4 shrink-0">
|
|
<!-- Pills Container -->
|
|
<div
|
|
class="border-base-300 m-3 flex min-h-12 flex-wrap items-center gap-2 rounded-lg border-2 border-dashed p-3"
|
|
>
|
|
<!-- Dynamic Pills -->
|
|
<template x-for="pkg in selectedPackages" :key="pkg.url">
|
|
<span class="badge badge-outline gap-2 max-w-xs">
|
|
<span class="truncate" :title="pkg.name" x-text="pkg.name"></span>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="h-4 w-4 cursor-pointer hover:text-error shrink-0 rotate-45"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
@click="removePackage(pkg.url)"
|
|
>
|
|
<path d="M5 12h14" />
|
|
<path d="M12 5v14" />
|
|
</svg>
|
|
</span>
|
|
</template>
|
|
|
|
<!-- Add Package Button -->
|
|
<button
|
|
type="button"
|
|
popovertarget="package_dropdown_menu"
|
|
style="anchor-name: --anchor-packages"
|
|
class="btn btn-sm btn-ghost gap-2 text-base-content/50"
|
|
>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
class="lucide lucide-plus-icon lucide-plus"
|
|
>
|
|
<path d="M5 12h14" />
|
|
<path d="M12 5v14" />
|
|
</svg>
|
|
Add Package
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Package Dropdown Menu -->
|
|
<ul
|
|
class="dropdown dropdown-center menu bg-base-200 rounded-box max-h-96 w-80 overflow-y-auto p-2 shadow-lg"
|
|
popover
|
|
x-ref="packageDropdown"
|
|
id="package_dropdown_menu"
|
|
style="position-anchor: --anchor-packages"
|
|
>
|
|
{% if unreviewed_packages %}
|
|
<li class="menu-title">Reviewed Packages</li>
|
|
{% for obj in reviewed_packages %}
|
|
<li>
|
|
<a
|
|
class="package-option flex items-center justify-between"
|
|
data-package-url="{{ obj.url }}"
|
|
data-package-name="{{ obj.name }}"
|
|
@click.prevent.stop="togglePackage('{{ obj.url }}', '{{ obj.name }}')"
|
|
>
|
|
<span>{{ obj.name }}</span>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="h-4 w-4"
|
|
:class="isPackageSelected('{{ obj.url }}') ? '' : 'hidden'"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M5 13l4 4L19 7"
|
|
/>
|
|
</svg>
|
|
</a>
|
|
</li>
|
|
{% endfor %}
|
|
<li class="menu-title">Unreviewed Packages</li>
|
|
{% for obj in unreviewed_packages %}
|
|
<li>
|
|
<a
|
|
class="package-option flex items-center justify-between"
|
|
data-package-url="{{ obj.url }}"
|
|
data-package-name="{{ obj.name }}"
|
|
@click.prevent.stop="togglePackage('{{ obj.url }}', '{{ obj.name }}')"
|
|
>
|
|
<span>{{ obj.name }}</span>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="h-4 w-4"
|
|
:class="isPackageSelected('{{ obj.url }}') ? '' : 'hidden'"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M5 13l4 4L19 7"
|
|
/>
|
|
</svg>
|
|
</a>
|
|
</li>
|
|
{% endfor %}
|
|
{% else %}
|
|
<li class="menu-title">Reviewed Packages</li>
|
|
{% for obj in reviewed_packages %}
|
|
<li>
|
|
<a
|
|
class="package-option flex items-center justify-between"
|
|
data-package-url="{{ obj.url }}"
|
|
data-package-name="{{ obj.name }}"
|
|
@click.prevent.stop="togglePackage('{{ obj.url }}', '{{ obj.name }}')"
|
|
>
|
|
<span>{{ obj.name }}</span>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="h-4 w-4"
|
|
:class="isPackageSelected('{{ obj.url }}') ? '' : 'hidden'"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M5 13l4 4L19 7"
|
|
/>
|
|
</svg>
|
|
</a>
|
|
</li>
|
|
{% endfor %}
|
|
{% endif %}
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- Loading Indicator -->
|
|
<div x-show="isSearching" class="flex shrink-0 justify-center py-8">
|
|
<span class="loading loading-spinner loading-lg text-primary"></span>
|
|
</div>
|
|
|
|
<!-- Results Container -->
|
|
<div class="min-h-0 flex-1 overflow-y-auto p-2">
|
|
<!-- No packages selected error -->
|
|
<template x-if="results && results.error === 'no_packages'">
|
|
<div class="alert alert-info">
|
|
<svg
|
|
class="w-6 h-6"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
/>
|
|
</svg>
|
|
<span>Please select at least one package</span>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Search error -->
|
|
<template x-if="error">
|
|
<div class="alert alert-error">
|
|
<svg
|
|
class="w-6 h-6"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
/>
|
|
</svg>
|
|
<span x-text="error"></span>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- No results -->
|
|
<template x-if="results && !results.error && !hasResults()">
|
|
<div class="alert alert-warning">
|
|
<svg
|
|
class="w-6 h-6"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
|
/>
|
|
</svg>
|
|
<span>No results found</span>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Results display -->
|
|
<template x-if="results && !results.error && hasResults()">
|
|
<div class="mb-2">
|
|
<div class="text-sm font-semibold text-base-content/70 mb-2">
|
|
Results
|
|
</div>
|
|
|
|
<!-- Compounds -->
|
|
<template x-if="results.Compounds && results.Compounds.length > 0">
|
|
<div class="collapse collapse-arrow bg-base-200 mb-2">
|
|
<input type="checkbox" checked />
|
|
<div class="collapse-title font-medium">
|
|
Compounds
|
|
<span
|
|
class="badge badge-neutral badge-sm ml-2"
|
|
x-text="results.Compounds.length"
|
|
></span>
|
|
</div>
|
|
<div class="collapse-content">
|
|
<template x-for="item in results.Compounds" :key="item.url">
|
|
<a
|
|
:href="item.url"
|
|
class="block px-4 py-2 hover:bg-base-300 rounded-lg transition-colors"
|
|
x-text="item.name"
|
|
></a>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Compound Structures -->
|
|
<template
|
|
x-if="results['Compound Structures'] && results['Compound Structures'].length > 0"
|
|
>
|
|
<div class="collapse collapse-arrow bg-base-200 mb-2">
|
|
<input type="checkbox" checked />
|
|
<div class="collapse-title font-medium">
|
|
Compound Structures
|
|
<span
|
|
class="badge badge-neutral badge-sm ml-2"
|
|
x-text="results['Compound Structures'].length"
|
|
></span>
|
|
</div>
|
|
<div class="collapse-content">
|
|
<template
|
|
x-for="item in results['Compound Structures']"
|
|
:key="item.url"
|
|
>
|
|
<a
|
|
:href="item.url"
|
|
class="block px-4 py-2 hover:bg-base-300 rounded-lg transition-colors"
|
|
x-text="item.name"
|
|
></a>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Rules -->
|
|
<template x-if="results.Rules && results.Rules.length > 0">
|
|
<div class="collapse collapse-arrow bg-base-200 mb-2">
|
|
<input type="checkbox" checked />
|
|
<div class="collapse-title font-medium">
|
|
Rules
|
|
<span
|
|
class="badge badge-neutral badge-sm ml-2"
|
|
x-text="results.Rules.length"
|
|
></span>
|
|
</div>
|
|
<div class="collapse-content">
|
|
<template x-for="item in results.Rules" :key="item.url">
|
|
<a
|
|
:href="item.url"
|
|
class="block px-4 py-2 hover:bg-base-300 rounded-lg transition-colors"
|
|
x-text="item.name"
|
|
></a>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Reactions -->
|
|
<template x-if="results.Reactions && results.Reactions.length > 0">
|
|
<div class="collapse collapse-arrow bg-base-200 mb-2">
|
|
<input type="checkbox" checked />
|
|
<div class="collapse-title font-medium">
|
|
Reactions
|
|
<span
|
|
class="badge badge-neutral badge-sm ml-2"
|
|
x-text="results.Reactions.length"
|
|
></span>
|
|
</div>
|
|
<div class="collapse-content">
|
|
<template x-for="item in results.Reactions" :key="item.url">
|
|
<a
|
|
:href="item.url"
|
|
class="block px-4 py-2 hover:bg-base-300 rounded-lg transition-colors"
|
|
x-text="item.name"
|
|
></a>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Pathways -->
|
|
<template x-if="results.Pathways && results.Pathways.length > 0">
|
|
<div class="collapse collapse-arrow bg-base-200 mb-2">
|
|
<input type="checkbox" checked />
|
|
<div class="collapse-title font-medium">
|
|
Pathways
|
|
<span
|
|
class="badge badge-neutral badge-sm ml-2"
|
|
x-text="results.Pathways.length"
|
|
></span>
|
|
</div>
|
|
<div class="collapse-content">
|
|
<template x-for="item in results.Pathways" :key="item.url">
|
|
<a
|
|
:href="item.url"
|
|
class="block px-4 py-2 hover:bg-base-300 rounded-lg transition-colors"
|
|
x-text="item.name"
|
|
></a>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Backdrop to close -->
|
|
<form method="dialog" class="modal-backdrop">
|
|
<button>close</button>
|
|
</form>
|
|
</dialog>
|