Files
gh-christianlouis-docuelevate/frontend/templates/base.html
T
Christian Krakau-Louis f714a9ed31 Merge pull request #556 from christianlouis/copilot/feature-universal-dashboard
feat(ui): Universal Sources & Destinations integrations dashboard
2026-03-08 19:08:11 +01:00

434 lines
25 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en" data-color-scheme-default="{{ ui_default_color_scheme | default('system') }}">
<head>
<meta charset="UTF-8" />
<title>{% block title %}DocuElevate{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Anti-flash dark mode script: runs before CSS/JS to prevent white flash -->
<script>
(function () {
var stored = localStorage.getItem('colorScheme');
var serverDefault = document.documentElement.getAttribute('data-color-scheme-default') || 'system';
var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
var scheme = stored || serverDefault;
if (scheme === 'dark' || (scheme === 'system' && prefersDark)) {
document.documentElement.classList.add('dark');
}
})();
</script>
<!-- Tailwind CSS or other global CSS references -->
<link rel="stylesheet" href="/static/styles.css" />
<!-- Alpine.js moved to head for earlier loading -->
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
{% block head_css %}
<!-- Tailwind CSS and other CSS -->
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"
integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA=="
crossorigin="anonymous" referrerpolicy="no-referrer" />
{% endblock %}
{% block head_extra %}{% endblock %}
<!-- CSRF token for AJAX/fetch requests -->
<meta name="csrf-token" content="{{ csrf_token | default('', true) }}">
</head>
<body class="bg-gray-50 min-h-screen flex flex-col"
data-multi-user="{{ 'true' if multi_user_enabled else 'false' }}"
data-allow-signup="{{ 'true' if allow_signup else 'false' }}">
<!-- Skip to main content link for keyboard/screen reader users -->
<a href="#main-content" class="skip-link">Skip to main content</a>
<!-- Global Nav -->
<nav class="bg-white shadow relative" aria-label="Main navigation">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex justify-between h-16 items-center">
<!-- Brand + Icon -->
<div class="flex-shrink-0">
<a href="/" class="inline-flex items-center space-x-2">
<!-- Icon -->
<span
class="material-symbols-light--folder-managed-outline text-blue-500"
style="width: 24px; height: 24px;"
></span>
<!-- Text -->
<span class="text-xl font-bold text-blue-500 hover:text-blue-700">
DocuElevate
</span>
</a>
</div>
<!-- ── Navigation: uses x-data for mobile menu + dropdowns ─────────── -->
<div x-data="{ mobileMenuOpen: false, adminMenuOpen: false }">
<!-- ── DESKTOP navigation ──────────────────────────────────────────── -->
<div class="hidden md:flex space-x-1 items-center">
{% if multi_user_enabled and not is_logged_in %}
{# ── Public / marketing nav (multi-user, visitor not signed in) ── #}
<a href="/pricing"
class="px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-100"
{% if request and request.url.path == '/pricing' %}aria-current="page"{% endif %}>
Pricing
</a>
<a href="/about"
class="px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-100"
{% if request and request.url.path == '/about' %}aria-current="page"{% endif %}>
About
</a>
{% else %}
{# ── App nav (logged-in or single-user / auth-disabled mode) ────── #}
<a href="/"
class="px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-100"
{% if request and request.url.path == '/' %}aria-current="page"{% endif %}>
<i class="fas fa-home mr-1 text-gray-400" aria-hidden="true"></i>Dashboard
</a>
<a href="/upload"
class="px-3 py-2 rounded-md text-sm font-medium text-blue-600 hover:text-blue-800 hover:bg-blue-50"
{% if request and request.url.path == '/upload' %}aria-current="page"{% endif %}>
<i class="fas fa-upload mr-1" aria-hidden="true"></i>Upload
</a>
<a href="/files"
class="px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-100"
{% if request and request.url.path == '/files' %}aria-current="page"{% endif %}>
<i class="fas fa-folder-open mr-1 text-gray-400" aria-hidden="true"></i>Files
</a>
<a href="/search"
class="px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-100"
{% if request and request.url.path == '/search' %}aria-current="page"{% endif %}>
<i class="fas fa-search mr-1 text-gray-400" aria-hidden="true"></i>Search
</a>
<a href="/pipelines"
class="px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-100"
{% if request and request.url.path == '/pipelines' %}aria-current="page"{% endif %}>
<i class="fas fa-project-diagram mr-1 text-gray-400" aria-hidden="true"></i>Pipelines
</a>
<a href="/integrations"
class="px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-100"
{% if request and request.url.path == '/integrations' %}aria-current="page"{% endif %}>
<i class="fas fa-plug mr-1 text-gray-400" aria-hidden="true"></i>Integrations
</a>
<!-- Admin dropdown shown only for admin users via JS -->
<div id="adminMenuContainer" class="relative hidden">
<button
@click="adminMenuOpen = !adminMenuOpen"
@click.outside="adminMenuOpen = false"
type="button"
class="inline-flex items-center px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-blue-500"
aria-haspopup="true"
aria-label="Admin menu"
:aria-expanded="adminMenuOpen"
>
<i class="fas fa-shield-alt mr-1 text-red-500" aria-hidden="true"></i>
Admin
<i class="fas fa-chevron-down ml-1 text-xs" aria-hidden="true"></i>
</button>
<div
x-show="adminMenuOpen"
x-transition:enter="transition ease-out duration-100 transform"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75 transform"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
class="absolute left-0 mt-2 w-52 bg-white rounded-md shadow-lg ring-1 ring-black ring-opacity-5 z-50"
role="menu"
aria-label="Admin actions"
>
<div class="py-1">
<a href="/settings" role="menuitem" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-cog w-4 mr-2 text-gray-500" aria-hidden="true"></i> Settings
</a>
<a href="/admin/users" role="menuitem" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-users w-4 mr-2 text-blue-500" aria-hidden="true"></i> Users
</a>
<a href="/admin/plans" role="menuitem" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-layer-group w-4 mr-2 text-indigo-500" aria-hidden="true"></i> Plan Designer
</a>
<a href="/admin/credentials" role="menuitem" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-key w-4 mr-2 text-yellow-500" aria-hidden="true"></i> Credentials
</a>
<a href="/admin/files" role="menuitem" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-folder-open w-4 mr-2 text-gray-500" aria-hidden="true"></i> File Manager
</a>
<div class="border-t border-gray-100 my-1"></div>
<a href="/duplicates" role="menuitem" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-clone w-4 mr-2 text-orange-500" aria-hidden="true"></i> Duplicates
</a>
<a href="/similarity" role="menuitem" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-sitemap w-4 mr-2 text-purple-500" aria-hidden="true"></i> Similarity
</a>
<a href="/admin/queue" role="menuitem" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-stream w-4 mr-2 text-blue-500" aria-hidden="true"></i> Queue Monitor
</a>
<a href="/admin/backup" role="menuitem" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-database w-4 mr-2 text-green-600" aria-hidden="true"></i> Backup &amp; Restore
</a>
<a href="/status" role="menuitem" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
{% if request and request.url.path == '/status' %}aria-current="page"{% endif %}>
<i class="fas fa-circle-dot w-4 mr-2 text-gray-500" aria-hidden="true"></i> Status
</a>
<div class="border-t border-gray-100 my-1"></div>
<a href="/admin/api-docs" role="menuitem" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-code w-4 mr-2 text-teal-500" aria-hidden="true"></i> API Docs
</a>
<a href="/developer-docs/" role="menuitem" class="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
<i class="fas fa-book w-4 mr-2 text-teal-500" aria-hidden="true"></i> Developer Docs
</a>
</div>
</div>
</div>
{% endif %}{# end multi_user_enabled / is_logged_in check #}
<!-- Help always visible, for every visitor regardless of auth state -->
<a href="/help"
class="px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-100"
aria-label="Help Center"
title="Help Center"
{% if request and request.url.path == '/help' %}aria-current="page"{% endif %}>
<i class="fas fa-circle-question mr-1 text-gray-400" aria-hidden="true"></i>Help
</a>
<!-- Dark mode toggle -->
<button
id="darkModeToggle"
onclick="toggleDarkMode()"
type="button"
class="p-1.5 rounded-md text-gray-500 hover:text-gray-700 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-blue-500"
aria-label="Toggle dark mode"
title="Toggle dark mode"
>
<i class="fas fa-moon"></i>
</button>
<!-- Dynamic Auth Section (populated by common.js) -->
<div id="authSection"></div>
</div>
<!-- ── Mobile menu button ──────────────────────────────────────────── -->
<button
@click="mobileMenuOpen = !mobileMenuOpen"
type="button"
class="md:hidden inline-flex items-center justify-center p-3 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500"
style="min-height:44px;min-width:44px;"
aria-label="Toggle navigation menu"
:aria-expanded="mobileMenuOpen"
>
<span class="sr-only">Open main menu</span>
<svg class="block h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
<!-- ── MOBILE menu ─────────────────────────────────────────────────── -->
<div
x-show="mobileMenuOpen"
x-transition:enter="transition ease-out duration-100 transform"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75 transform"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
class="md:hidden absolute top-16 inset-x-0 bg-white shadow-md z-50"
>
<div class="px-2 pt-2 pb-3 space-y-1 sm:px-3">
{% if multi_user_enabled and not is_logged_in %}
{# ── Public / marketing links ────────────────────────────────── #}
<a href="/pricing"
class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50"
{% if request and request.url.path == '/pricing' %}aria-current="page"{% endif %}>
<i class="fas fa-tag mr-2 text-gray-400" aria-hidden="true"></i>Pricing
</a>
<a href="/about"
class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50"
{% if request and request.url.path == '/about' %}aria-current="page"{% endif %}>
<i class="fas fa-info-circle mr-2 text-gray-400" aria-hidden="true"></i>About
</a>
{% else %}
{# ── App links (logged-in or single-user) ────────────────────── #}
<a href="/"
class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50"
{% if request and request.url.path == '/' %}aria-current="page"{% endif %}>
<i class="fas fa-home mr-2 text-gray-400" aria-hidden="true"></i>Dashboard
</a>
<a href="/upload"
class="block px-3 py-3 rounded-md text-base font-medium text-blue-600 hover:text-blue-800 hover:bg-blue-50"
{% if request and request.url.path == '/upload' %}aria-current="page"{% endif %}>
<i class="fas fa-upload mr-2" aria-hidden="true"></i>Upload
</a>
<a href="/files"
class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50"
{% if request and request.url.path == '/files' %}aria-current="page"{% endif %}>
<i class="fas fa-folder-open mr-2 text-gray-400" aria-hidden="true"></i>Files
</a>
<a href="/search"
class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50"
{% if request and request.url.path == '/search' %}aria-current="page"{% endif %}>
<i class="fas fa-search mr-2 text-gray-400" aria-hidden="true"></i>Search
</a>
<a href="/pipelines"
class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50"
{% if request and request.url.path == '/pipelines' %}aria-current="page"{% endif %}>
<i class="fas fa-project-diagram mr-2 text-gray-400" aria-hidden="true"></i>Pipelines
</a>
<a href="/integrations"
class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50"
{% if request and request.url.path == '/integrations' %}aria-current="page"{% endif %}>
<i class="fas fa-plug mr-2 text-gray-400" aria-hidden="true"></i>Integrations
</a>
<!-- Admin section in mobile menu shown only for admin users via JS -->
<div id="mobileAdminSection" class="hidden">
<div class="border-t border-gray-200 mt-1 pt-1">
<p class="px-3 py-1 text-xs font-semibold text-red-600 uppercase tracking-wider flex items-center">
<i class="fas fa-shield-alt mr-1" aria-hidden="true"></i> Admin
</p>
<a href="/settings" class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50">
<i class="fas fa-cog mr-2 text-gray-400" aria-hidden="true"></i> Settings
</a>
<a href="/admin/users" class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50">
<i class="fas fa-users mr-2 text-blue-400" aria-hidden="true"></i> Users
</a>
<a href="/admin/plans" class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50">
<i class="fas fa-layer-group mr-2 text-indigo-400" aria-hidden="true"></i> Plan Designer
</a>
<a href="/admin/credentials" class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50">
<i class="fas fa-key mr-2 text-yellow-400" aria-hidden="true"></i> Credentials
</a>
<a href="/admin/files" class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50">
<i class="fas fa-folder-open mr-2 text-gray-400" aria-hidden="true"></i> File Manager
</a>
<a href="/duplicates" class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50">
<i class="fas fa-clone mr-2 text-orange-400" aria-hidden="true"></i> Duplicates
</a>
<a href="/similarity" class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50">
<i class="fas fa-sitemap mr-2 text-purple-400" aria-hidden="true"></i> Similarity
</a>
<a href="/admin/queue" class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50">
<i class="fas fa-stream mr-2 text-blue-400" aria-hidden="true"></i> Queue Monitor
</a>
<a href="/admin/backup" class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50">
<i class="fas fa-database mr-2 text-green-500" aria-hidden="true"></i> Backup &amp; Restore
</a>
<a href="/status" class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50"
{% if request and request.url.path == '/status' %}aria-current="page"{% endif %}>
<i class="fas fa-circle-dot mr-2 text-gray-400" aria-hidden="true"></i> Status
</a>
<a href="/admin/api-docs" class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50">
<i class="fas fa-code mr-2 text-teal-400" aria-hidden="true"></i> API Docs
</a>
<a href="/developer-docs/" class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50">
<i class="fas fa-book mr-2 text-teal-400" aria-hidden="true"></i> Developer Docs
</a>
</div>
</div>
{% endif %}{# end multi_user_enabled / is_logged_in check #}
<!-- Help always visible, for every visitor regardless of auth state -->
<a href="/help"
class="block px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50"
aria-label="Help Center"
{% if request and request.url.path == '/help' %}aria-current="page"{% endif %}>
<i class="fas fa-circle-question mr-2 text-gray-400" aria-hidden="true"></i>Help
</a>
<!-- Dark mode toggle (mobile) -->
<button
onclick="toggleDarkMode()"
type="button"
class="flex items-center w-full px-3 py-3 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50"
id="darkModeToggleMobile"
aria-label="Toggle dark mode"
>
<i class="fas fa-moon mr-2" id="darkModeIconMobile" aria-hidden="true"></i>
<span id="darkModeTextMobile">Dark Mode</span>
</button>
<!-- Mobile Auth / Account section (populated by common.js) -->
<div id="mobileAuthSection" class="border-t border-gray-100 pt-1 mt-1">
<!-- Will be populated by JS -->
</div>
</div>
</div>
</div>
</div>
</nav>
<!-- Main Content -->
<main id="main-content" class="flex-grow">
{% block content %}{% endblock %}
</main>
<!-- Footer -->
<footer class="bg-white shadow" role="contentinfo">
<div class="max-w-7xl mx-auto px-4 py-4 text-center text-gray-600">
DocuElevate 2025 -
<nav aria-label="Footer navigation" class="inline">
<a href="/privacy" class="text-blue-500 hover:underline">Privacy</a> -
<a href="/imprint" class="text-blue-500 hover:underline">Imprint</a> -
<a href="/terms" class="text-blue-500 hover:underline">Terms</a> -
<a href="/cookies" class="text-blue-500 hover:underline">Cookies</a> -
<a href="/license" class="text-blue-500 hover:underline">License</a> -
<a href="/attribution" class="text-blue-500 hover:underline">Attributions</a>
</nav> -
<span class="text-xs">Version {{ app_version|default(version, true) }}{% if release_name %} "{{ release_name }}"{% endif %}</span>
</div>
</footer>
<!-- Cookie Notice Banner -->
<!-- DocuElevate uses only strictly necessary session cookies (no tracking/analytics).
Under GDPR Art. 5(3) ePrivacy Directive, strictly necessary cookies do not require
prior consent, but transparency is required. This banner provides that notice. -->
<div id="cookieNotice"
class="fixed bottom-0 inset-x-0 z-50 bg-gray-800 text-white text-sm px-4 py-3 flex flex-col sm:flex-row items-center justify-between gap-2"
style="display:none!important"
aria-live="polite"
role="region"
aria-label="Cookie notice">
<p class="text-center sm:text-left">
DocuElevate uses only essential session cookies required for authentication and service operation.
No tracking or analytics cookies are used.
<a href="/cookies" class="underline hover:text-blue-300 ml-1">Cookie Policy</a> &middot;
<a href="/privacy" class="underline hover:text-blue-300 ml-1">Privacy Notice</a>
</p>
<button
id="cookieNoticeAccept"
type="button"
class="flex-shrink-0 bg-blue-600 hover:bg-blue-700 text-white font-semibold px-4 py-1.5 rounded focus:outline-none focus:ring-2 focus:ring-blue-400"
aria-label="Acknowledge cookie notice">
Got it
</button>
</div>
<script>
(function () {
var NOTICE_KEY = 'cookieNoticeDismissed';
var notice = document.getElementById('cookieNotice');
var btn = document.getElementById('cookieNoticeAccept');
if (!localStorage.getItem(NOTICE_KEY)) {
notice.style.removeProperty('display');
}
if (btn) {
btn.addEventListener('click', function () {
localStorage.setItem(NOTICE_KEY, '1');
notice.style.display = 'none';
});
}
})();
</script>
<!-- Common JS (shared) -->
<script src="/static/js/common.js"></script>
<!-- Let child pages define extra scripts if needed -->
{% block scripts %}{% endblock %}
</body>
</html>