New Layout for parallel styling

Signed-off-by: Tobias O <tobias.olenyi@envipath.com>
This commit is contained in:
2025-10-17 20:25:50 +13:00
parent f76861a83f
commit f5889b270a
12 changed files with 859 additions and 1094 deletions

View File

@ -1,15 +1,16 @@
<!DOCTYPE html>
<html>
<html data-theme="envipath">
{% load static %}
<head>
<title>{{ title }}</title>
<style>
html, body {
height: 100%; /* ensure body fills viewport */
overflow-x: hidden; /* prevent horizontal scroll */
}
</style>
{# TODO use bundles from bootstrap 3.3.7 #}
<meta name="csrf-token" content="{{ csrf_token }}">
{# Favicon #}
<link rel="shortcut icon" type="image/png" href="{% static 'images/favicon.ico' %}"/>
{# Tailwind CSS Output - MUST load first for proper cascading #}
<link href="{% static 'css/output.css' %}" rel="stylesheet" type="text/css"/>
{# Legacy Bootstrap 3.3.7 - scoped to .legacy-bootstrap #}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
@ -20,7 +21,21 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.7.3/js/bootstrap-select.min.js"></script>
<script src="https://community.envipath.org/javascripts/embed-topics.js"></script>
<!-- CDN END -->
<meta name="csrf-token" content="{{ csrf_token }}">
{# Bootstrap compatibility styles #}
<style>
/* Isolate Bootstrap to legacy sections only */
.legacy-bootstrap {
/* Bootstrap can override Tailwind here */
}
/* Prevent Bootstrap from affecting Tailwind sections */
html, body {
height: 100%; /* ensure body fills viewport */
overflow-x: hidden; /* prevent horizontal scroll */
}
</style>
<script>
const csrftoken = document.querySelector('[name=csrf-token]').content;
@ -33,8 +48,7 @@
}
});
</script>
{# Favicon #}
<link rel="shortcut icon" type="image/png" href="{% static 'images/favicon.ico' %}"/>
<!-- {# C3 CSS #}-->
<!-- <link id="css-c3" href="{% static 'css/c3.css' %}" rel="stylesheet" type="text/css"/>-->
<!-- {# EP CSS #}-->
@ -68,6 +82,8 @@
</head>
<body>
<!-- Legacy Bootstrap navbar - isolated from Tailwind -->
<div class="legacy-bootstrap">
<nav class="navbar navbar-default navbar-inverse" style="border-radius:0px;" role="navigation">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
@ -177,6 +193,9 @@
</div>
</div>
</nav>
</div>
<!-- End legacy Bootstrap navbar -->
<div id="docContent" class="content container">
{% if breadcrumbs %}
<div id="bread">
@ -221,7 +240,8 @@
{% endif %}
</div>
<!-- FOOTER -->
<!-- FOOTER - Legacy Bootstrap -->
<div class="legacy-bootstrap">
<div class="container text-center">
<hr/>
<div class="row">
@ -256,6 +276,9 @@
</ul>
</div>
</div>
</div>
<!-- End legacy Bootstrap footer -->
<script>
$(function () {
// Hide actionsbutton if theres no action defined

View File

@ -0,0 +1,131 @@
<!DOCTYPE html>
<html data-theme="envipath" lang="en">
{% load static %}
<head>
<title>{{ title }}</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="csrf-token" content="{{ csrf_token }}">
{# Favicon #}
<link rel="shortcut icon" type="image/png" href="{% static 'images/favicon.ico' %}"/>
{# Tailwind CSS + DaisyUI Output #}
<link href="{% static 'css/output.css' %}" rel="stylesheet" type="text/css"/>
{# jQuery - Keep for compatibility with existing JS #}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
{# Font Awesome #}
<link href="https://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
{# Discourse embed for community #}
<script src="https://community.envipath.org/javascripts/embed-topics.js"></script>
<script>
const csrftoken = document.querySelector('[name=csrf-token]').content;
// Setup CSRF header for all jQuery AJAX requests
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type)) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
</script>
{# General EP JS #}
<script src="{% static 'js/pps.js' %}"></script>
{# Modal Steps for Stepwise Modal Wizards #}
<script src="{% static 'js/jquery-bootstrap-modal-steps.js' %}"></script>
{% if not debug %}
<!-- Matomo -->
<script>
var _paq = window._paq = window._paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function () {
var u = "//matomo.envipath.com/";
_paq.push(['setTrackerUrl', u + 'matomo.php']);
_paq.push(['setSiteId', '10']);
var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
g.async = true;
g.src = u + 'matomo.js';
s.parentNode.insertBefore(g, s);
})();
</script>
<!-- End Matomo Code -->
{% endif %}
</head>
<body class="min-h-screen flex flex-col">
{% include "includes/navbar.html" %}
{# Main Content Area #}
<main class="flex-1">
<div id="docContent" class="container mx-auto">
{% if breadcrumbs %}
<div id="bread" class="py-4">
<div class="text-sm breadcrumbs">
<ul>
{% for elem in breadcrumbs %}
{% for name, url in elem.items %}
{% if forloop.parentloop.last %}
<li class="font-semibold">{{ name }}</li>
{% else %}
<li><a href="{{ url }}" class="link link-hover">{{ name }}</a></li>
{% endif %}
{% endfor %}
{% endfor %}
</ul>
</div>
</div>
{% endif %}
{% if message %}
<div id="message" class="alert alert-info my-4">
{{ message }}
</div>
{% endif %}
{% block content %}
{% endblock content %}
{% if meta.url_contains_package and meta.current_package.license %}
<div class="collapse collapse-arrow bg-base-200 my-8">
<input type="checkbox" checked />
<div class="collapse-title text-xl font-medium">
License
</div>
<div class="collapse-content">
<a target="_blank" href="{{ meta.current_package.license.link }}">
<img src="{{ meta.current_package.license.image_link }}" alt="License">
</a>
</div>
</div>
{% endif %}
</div>
</main>
{% include "includes/footer.html" %}
{# Modals - TODO: Convert these to DaisyUI modals #}
{% block modals %}
{# Note: These modals still use Bootstrap markup and will need conversion #}
{% include "modals/cite_modal.html" %}
{% include "modals/signup_modal.html" %}
{% include "modals/predict_modal.html" %}
{% include "modals/batch_predict_modal.html" %}
{% endblock %}
<script>
$(function () {
// Hide actionsbutton if there's no action defined
if ($('#actionsButton ul').children().length > 0) {
$('#actionsButton').show();
}
});
</script>
</body>
</html>

View File

@ -0,0 +1,17 @@
{% load static %}
{# Modern Tailwind Footer #}
<footer class="footer footer-center p-10 bg-base-200 text-base-content">
<div class="grid grid-flow-col gap-8">
<a href="http://eawag.ch" target="_blank">
<img height="60" src='{% static "/images/ealogo.gif" %}' alt="Eawag"/>
</a>
<a href="http://ml.auckland.ac.nz" target="_blank">
<img height="60" src='{% static "/images/uoa.png" %}' alt="The University of Auckland"/>
</a>
</div>
<div>
<div class="grid grid-flow-col gap-4">
<a href="mailto:admin@envipath.org" class="link link-hover">Contact</a>
</div>
</div>
</footer>

View File

@ -0,0 +1,70 @@
{% load static %}
{# Modern DaisyUI Navbar #}
<div class="navbar bg-neutral text-neutral-content shadow-lg">
<div class="navbar-start">
<a href="{{ meta.server_url }}" class="btn btn-ghost normal-case text-xl">
<img src='{% static "/images/logo-short-white.svg" %}' width="100" alt="enviPath">
</a>
</div>
<div class="navbar-center hidden lg:flex">
<ul class="menu menu-horizontal px-1">
<li><a href="#" id="predictLink">Predict Pathway</a></li>
<li><a href="{{ meta.server_url }}/package" id="packageLink">Package</a></li>
<li><a href="{{ meta.server_url }}/search" id="searchLink">Search</a></li>
<li><a href="{{ meta.server_url }}/model" id="modelLink">Modelling</a></li>
<li>
<details>
<summary>Browse Data</summary>
<ul class="bg-base-100 rounded-t-none p-2 w-48 z-50">
<li><a href="{{ meta.server_url }}/pathway" id="pathwayLink">Pathway</a></li>
<li><a href="{{ meta.server_url }}/rule" id="ruleLink">Rule</a></li>
<li><a href="{{ meta.server_url }}/compound" id="compoundLink">Compound</a></li>
<li><a href="{{ meta.server_url }}/reaction" id="reactionLink">Reaction</a></li>
<li><a href="{{ meta.server_url }}/model" id="relative-reasoningLink">Model</a></li>
<li><a href="{{ meta.server_url }}/scenario" id="scenarioLink">Scenario</a></li>
</ul>
</details>
</li>
</ul>
</div>
<div class="navbar-end">
<ul class="menu menu-horizontal px-1">
<li><a href="https://community.envipath.org/" id="communityLink">Community</a></li>
<li>
<details>
<summary>Info</summary>
<ul class="bg-base-100 rounded-t-none p-2 w-48 z-50">
<li><a href="https://community.envipath.org/t/envipath-license/109" id="licenceLink">Licences</a></li>
<li class="divider"></li>
<li><a target="_blank" href="https://wiki.envipath.org/" id="wikiLink">Documentation Wiki</a></li>
<li><a href="#" id="citeButton">How to cite enviPath</a></li>
<li class="divider"></li>
<li><a>Version: {{ meta.version }}</a></li>
</ul>
</details>
</li>
{% if meta.user.username == 'anonymous' %}
<li><a href="#signup" id="loginButton">Login</a></li>
{% else %}
<li>
<details>
<summary id="loggedInButton">{{ user.username }}</summary>
<ul class="bg-base-100 rounded-t-none p-2 w-48 z-50">
<li><a href="{{ meta.user.url }}" id="accountbutton">My Account</a></li>
<li class="divider"></li>
<li>
<form action="{% url 'logout' %}" method="post">
{% csrf_token %}
<input type="hidden" name="logout" value="true">
<button type="submit" class="btn btn-ghost btn-sm w-full">Logout</button>
</form>
</li>
</ul>
</details>
</li>
{% endif %}
</ul>
</div>
</div>

View File

@ -1,7 +1,7 @@
{% extends "framework.html" %}
{% extends "framework_modern.html" %}
{% load static %}
{% block content %}
<!-- TODO rename ids as well as remove pathways if modal is closed!-->
<!-- Modal for Found Matching Pathways - TODO: Convert to DaisyUI modal -->
<div class="modal fade" tabindex="-1" id="foundMatching" role="dialog" aria-labelledby="foundModal"
aria-hidden="true">
<div class="modal-dialog">
@ -30,157 +30,154 @@
</div>
</div>
</div>
<div class="row">
<div class="col-xs-8">
<div class="jumbotron">
<h1>
<img id="image-logo-long" class="img-responsive" alt="enviPath" width="1000ex"
src='{% static "/images/logo-long.svg" %}'/>
</h1>
<p>enviPath is a database and prediction system for the microbial
biotransformation of organic environmental contaminants. The
database provides the possibility to store and view experimentally
observed biotransformation pathways. The pathway prediction system
provides different relative reasoning models to predict likely biotransformation
pathways and products. You can try it out below.
</p>
<p>
<a class="btn" style="background-color:#222222;color:#9d9d9d" role="button" target="_blank"
href="https://wiki.envipath.org/index.php/Main_Page">Learn more &gt;&gt;</a>
</p>
<!-- Hero Section with Logo and Search -->
<section class="relative min-h-screen flex flex-col items-center justify-center overflow-hidden">
<!-- Blurred Background -->
<div class="absolute inset-0 bg-gradient-to-br from-blue-50 via-white to-teal-50 backdrop-blur-3xl"></div>
<!-- Logo - Full width, centered -->
<div class="relative z-10 w-full max-w-5xl px-4 mb-12 text-center">
<img id="image-logo-long"
class="w-full h-auto"
alt="enviPath"
src='{% static "/images/logo-long.svg" %}'/>
</div>
<!-- Search Component - Center of Hero -->
<div class="relative z-10 w-full max-w-4xl px-4">
<form id="index-form" action="{{ meta.current_package.url }}/pathway" method="POST">
{% csrf_token %}
<input type="text" placeholder="Type here" class="input" />
<input type="hidden" id="index-form-smiles" name="smiles" value="smiles">
<input type="hidden" id="index-form-predict" name="predict" value="predict">
<input type="hidden" id="current-action" value="predict">
</form>
</div>
<!-- Learn More Link -->
<div class="relative z-10 mt-8">
<a class="btn btn-ghost btn-lg" role="button" target="_blank"
href="https://wiki.envipath.org/index.php/Main_Page">
Learn more &raquo;
</a>
</div>
</section>
<!-- Community News Section -->
<section class="py-16 bg-base-200">
<div class="max-w-7xl mx-auto px-4">
<h2 class="text-4xl font-bold text-center mb-12">Community Updates</h2>
<div id="community-news-container" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- News cards will be populated here -->
<div id="loading" class="col-span-full flex justify-center">
<span class="loading loading-spinner loading-lg"></span>
</div>
</div>
</div>
<div id="loading"></div>
<div class="col-xs-4">
<d-topics-list discourse-url="https://community.envipath.org" per-page="10" category="10"
template="complete"></d-topics-list>
</div>
</div>
<div class="row">
<form id="index-form" action="{{ meta.current_package.url }}/pathway" method="POST">
{% csrf_token %}
<div class="input-group" id="index-form-bar">
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
aria-expanded="false">
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li>
<iframe id="index-form-ketcher" src="{% static '/js/ketcher2/ketcher.html' %}" width="100%"
height="510"></iframe>
</li>
</ul>
</div>
<input type="text" class="form-control" id='index-form-text-input'
placeholder="Enter a SMILES to predict a Pathway or type something to search">
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false" id="action-button">Predict <span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a id="dropdown-predict">Predict</a></li>
<li><a id="dropdown-search">Search</a></li>
</ul>
<button class="btn" style="background-color:#222222;color:#9d9d9d" type="button" id="run-button">Go!
</button>
</div>
<!-- Fallback for discourse widget -->
<div class="hidden">
<d-topics-list discourse-url="https://community.envipath.org"
per-page="10"
category="10"
template="complete"
id="discourse-topics">
</d-topics-list>
</div>
<input type="hidden" id="index-form-smiles" name="smiles" value="smiles">
<input type="hidden" id="index-form-predict" name="predict" value="predict">
</form>
</div>
<p></p>
</div>
</section>
<!-- Mission Statement Section -->
<section class="py-20 bg-white">
<div class="max-w-5xl mx-auto px-4">
<div class="prose prose-lg mx-auto text-center">
<h2 class="text-4xl font-bold mb-8">About enviPath</h2>
<p class="text-xl text-gray-700 leading-relaxed mb-6">
enviPath is a database and prediction system for the microbial
biotransformation of organic environmental contaminants. The
database provides the possibility to store and view experimentally
observed biotransformation pathways.
</p>
<p class="text-xl text-gray-700 leading-relaxed">
The pathway prediction system provides different relative reasoning models
to predict likely biotransformation pathways and products. Explore our tools
and contribute to advancing environmental biotransformation research.
</p>
</div>
</div>
</section>
<script language="javascript">
var currentPackage = "{{ meta.current_package.url }}";
function goButtonClicked() {
$(this).prop("disabled", true);
// Process discourse topics into individual cards
function processDiscourseTopics() {
setTimeout(function() {
const topicsList = $('#discourse-topics');
if (topicsList.length && topicsList.find('li').length > 0) {
$('#loading').hide();
var action = $('#action-button').text().trim();
topicsList.find('li').each(function(index) {
if (index >= 6) return false; // Limit to 6 cards
var textSmiles = $('#index-form-text-input').val().trim();
const $item = $(this);
const $link = $item.find('a').first();
const title = $link.text().trim();
const url = $link.attr('href');
const excerpt = $item.find('.topic-excerpt').text().trim() || 'Click to read more...';
if (textSmiles === '') {
$(this).prop("disabled", false);
return;
}
const card = `
<div class="card bg-white shadow-xl hover:shadow-2xl transition-shadow duration-300">
<div class="card-body">
<h3 class="card-title text-lg line-clamp-2">
<a href="${url}" target="_blank" class="hover:text-primary">
${title}
</a>
</h3>
<p class="text-gray-600 line-clamp-3">${excerpt}</p>
<div class="card-actions justify-end mt-4">
<a href="${url}" target="_blank" class="btn btn-sm btn-primary">
Read More
</a>
</div>
</div>
</div>
`;
var ketcherSmiles = getKetcher('index-form-ketcher').getSmiles().trim();
if (action !== 'Search' && ketcherSmiles !== '' && textSmiles !== ketcherSmiles) {
console.log("Ketcher and TextInput differ!");
}
if (action === 'Search') {
var par = {};
par['search'] = textSmiles;
par['mode'] = 'text';
var queryString = $.param(par, true);
window.location.href = "/search?" + queryString;
} else {
$('#index-form-smiles').val(textSmiles);
$('#index-form').submit();
}
}
function actionDropdownClicked() {
var suffix = ' <span class="caret"></span>';
var dropdownVal = $(this).text();
if (dropdownVal === 'Search') {
$("#index-form").attr("action", '/search');
$("#index-form").attr("method", 'GET');
} else {
$("#index-form").attr("action", currentPackage + "/pathway");
}
$('#action-button').html(dropdownVal + suffix);
}
function ketcherToTextInput() {
$('#index-form-text-input').val(this.ketcher.getSmiles());
$('#community-news-container').append(card);
});
} else {
// Retry if not loaded yet
if (index < 10) {
processDiscourseTopics();
} else {
$('#loading').html('<p class="text-gray-500">Unable to load community updates</p>');
}
}
}, 500);
}
$(function () {
// Handle form submission on Enter
$('#index-form').on("submit", function (e) {
e.preventDefault();
$('#index-form').on("keydown", function (e) {
if (e.key === "Enter") {
e.preventDefault();
goButtonClicked();
var textSmiles = $('#index-form-text-input').val().trim();
if (textSmiles === '') {
return;
}
$('#index-form-smiles').val(textSmiles);
$("#index-form").attr("action", currentPackage + "/pathway");
$("#index-form").attr("method", 'POST');
this.submit();
});
// Code that should be executed once DOM is ready goes here
$('#dropdown-predict').on('click', actionDropdownClicked);
$('#dropdown-search').on('click', actionDropdownClicked);
$('#run-button').on('click', goButtonClicked);
// Update Ketcher Width
var fullWidth = $('#index-form-bar').width();
$('#index-form-ketcher').width(fullWidth);
// add a listener that gets triggered whenever the structure in ketcher has changed
$('#index-form-ketcher').on('load', function () {
const checkKetcherReady = () => {
win = this.contentWindow
if (win.ketcher && 'editor' in win.ketcher) {
win.ketcher.editor.event.change.handlers.push({
once: false,
priority: 0,
f: ketcherToTextInput,
ketcher: win.ketcher
});
} else {
setTimeout(checkKetcherReady, 100);
}
};
checkKetcherReady();
});
// Process discourse topics into cards
processDiscourseTopics();
});
</script>
{% endblock content %}