| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- <!DOCTYPE html>
- {% load tailwind_tags public_urls static i18n %}
- {% get_current_language as LANGUAGE_CODE %}
- <html lang="{{ LANGUAGE_CODE }}" x-data="{ open:false }">
- <head>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1" />
- <title>{{ current_site.name|default:'Web Admin' }} — {% block title %}{% endblock %}</title>
- {% tailwind_css %}
- <script defer src="{% static 'alpinejs/dist/cdn.min.js' %}"></script>
- </head>
- <body class="bg-gray-50 text-gray-900">
- <div class="min-h-screen">
- <nav class="bg-white border-b border-gray-200">
- <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
- <div class="flex h-16 items-center justify-between">
- <div class="flex items-center gap-8">
- <a href="/webadmin/" class="text-lg font-semibold">{{ current_site.name|default:'Web Admin' }}</a>
- <div class="hidden md:flex items-center gap-4">
- <a class="hover:text-black/70" href="{% url 'admin_frontend:dashboard' %}">Dashboard</a>
- <a class="hover:text-black/70" href="{% url 'admin_frontend:profiles_list' %}">Profiles</a>
- <a class="hover:text-black/70" href="{% url 'admin_frontend:opportunities_list' %}">Opportunities</a>
- <a class="hover:text-black/70" href="{% url 'admin_frontend:intro_requests_list' %}">Intro Requests</a>
- <a class="hover:text-black/70" href="{% url 'admin_frontend:leaderboard' %}">Leaderboard</a>
- </div>
- </div>
- <div class="flex items-center gap-3">
- {% if request.user.is_authenticated %}
- <span class="text-sm text-gray-600">{{ request.user.username }}</span>
- <form method="post" action="{% url 'admin_frontend:logout' %}">
- {% csrf_token %}
- <button class="text-sm text-red-600 hover:text-red-700">Logout</button>
- </form>
- <form method="post" action="{% url 'set_language' %}" class="ml-2">
- {% csrf_token %}
- <input type="hidden" name="next" value="{{ request.get_full_path }}" />
- {% get_available_languages as langs %}
- {% get_current_language as LANGUAGE_CODE %}
- {% get_language_info_list for langs as languages %}
- <label for="lang" class="sr-only">Language</label>
- <select id="lang" name="language" class="text-sm border border-gray-300 rounded px-2 py-1 bg-white"
- onchange="this.form.submit()">
- {% for lang in languages %}
- <option value="{{ lang.code }}" {% if lang.code == LANGUAGE_CODE %}selected{% endif %}>{{ lang.name_local }}</option>
- {% endfor %}
- </select>
- </form>
- {% else %}
- <a class="text-sm text-blue-600 hover:text-blue-700" href="{% url 'admin_frontend:login' %}">Login</a>
- {% endif %}
- </div>
- </div>
- </div>
- </nav>
- <main class="mx-auto max-w-7xl p-4 sm:p-6 lg:p-8">
- {% if messages %}
- <div id="toasts" class="fixed bottom-4 right-4 z-50 space-y-2">
- {% for message in messages %}
- <div class="toast px-4 py-3 rounded border shadow {{ message.tags|default:'' }}">
- {% trans message %}
- </div>
- {% endfor %}
- </div>
- {% endif %}
- <div class="grid grid-cols-1 md:grid-cols-12 gap-6">
- <aside class="md:col-span-3">
- <nav class="bg-white rounded shadow p-4">
- <div class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">Navigation</div>
- {% app_menu 'admin_frontend' as menu_items %}
- <ul class="space-y-1">
- <div x-data="{ open: {% if '/webadmin/settings/' in request.path %}true{% else %}false{% endif %} }">
- <div @click="open = !open" class="flex items-center justify-between rounded px-3 py-2 hover:bg-gray-50 cursor-pointer">
- <span class="truncate">{% trans "Settings" %}</span>
- <svg :class="{'rotate-180': open}" class="w-4 h-4 transform transition-transform" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
- </svg>
- </div>
- <div x-show="open" x-cloak class="pl-4">
- {% app_menu 'settings' as settings_menu_items %}
- <ul class="space-y-1">
- {% for it in settings_menu_items %}
- <li>
- <a href="{{ it.url }}" class="flex items-center justify-between rounded px-3 py-2 hover:bg-gray-50 {% if request.path == it.url %}bg-blue-50 text-blue-700{% else %}text-gray-700{% endif %}">
- <span class="truncate">{{ it.label }}</span>
- </a>
- </li>
- {% endfor %}
- </ul>
- </div>
- </div>
- <div x-data="{ open: {% if '/webadmin/cms/' in request.path %}true{% else %}false{% endif %} }">
- <div @click="open = !open" class="flex items-center justify-between rounded px-3 py-2 hover:bg-gray-50 cursor-pointer">
- <span class="truncate">{% trans "CMS" %}</span>
- <svg :class="{'rotate-180': open}" class="w-4 h-4 transform transition-transform" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
- </svg>
- </div>
- <div x-show="open" x-cloak class="pl-4">
- {% app_menu 'cms' as cms_menu_items %}
- <ul class="space-y-1">
- {% for it in cms_menu_items %}
- <li>
- <a href="{{ it.url }}" class="flex items-center justify-between rounded px-3 py-2 hover:bg-gray-50 {% if request.path == it.url %}bg-blue-50 text-blue-700{% else %}text-gray-700{% endif %}">
- <span class="truncate">{{ it.label }}</span>
- </a>
- </li>
- {% endfor %}
- </ul>
- </div>
- </div>
- <div x-data="{ open: {% if '/webadmin/frontend/' in request.path %}true{% else %}false{% endif %} }">
- <div @click="open = !open" class="flex items-center justify-between rounded px-3 py-2 hover:bg-gray-50 cursor-pointer">
- <span class="truncate">{% trans "Frontend" %}</span>
- <svg :class="{'rotate-180': open}" class="w-4 h-4 transform transition-transform" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
- </svg>
- </div>
- <div x-show="open" x-cloak class="pl-4">
- {% app_menu 'public_frontend_admin' as pf_menu_items %}
- <ul class="space-y-1">
- {% for it in pf_menu_items %}
- <li>
- <a href="{{ it.url }}" class="flex items-center justify-between rounded px-3 py-2 hover:bg-gray-50 {% if request.path == it.url %}bg-blue-50 text-blue-700{% else %}text-gray-700{% endif %}">
- <span class="truncate">{{ it.label }}</span>
- </a>
- </li>
- {% endfor %}
- </ul>
- </div>
- </div>
- <div x-data="{ open: {% if '/webadmin/orgs/' in request.path %}true{% else %}false{% endif %} }">
- <div @click="open = !open" class="flex items-center justify-between rounded px-3 py-2 hover:bg-gray-50 cursor-pointer">
- <span class="truncate">{% trans "Organization" %}</span>
- <svg :class="{'rotate-180': open}" class="w-4 h-4 transform transition-transform" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
- </svg>
- </div>
- <div x-show="open" x-cloak class="pl-4">
- {% app_menu 'orgs_admin' as org_menu_items %}
- <ul class="space-y-1">
- {% for it in org_menu_items %}
- <li>
- <a href="{{ it.url }}" class="flex items-center justify-between rounded px-3 py-2 hover:bg-gray-50 {% if request.path == it.url %}bg-blue-50 text-blue-700{% else %}text-gray-700{% endif %}">
- <span class="truncate">{{ it.label }}</span>
- </a>
- </li>
- {% endfor %}
- </ul>
- </div>
- </div>
- <div x-data="{ open: {% if '/webadmin/billings/' in request.path %}true{% else %}false{% endif %} }">
- <div @click="open = !open" class="flex items-center justify-between rounded px-3 py-2 hover:bg-gray-50 cursor-pointer">
- <span class="truncate">{% trans "Billing" %}</span>
- <svg :class="{'rotate-180': open}" class="w-4 h-4 transform transition-transform" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
- </svg>
- </div>
- <div x-show="open" x-cloak class="pl-4">
- {% app_menu 'billing' as billing_menu_items %}
- <ul class="space-y-1">
- {% for it in billing_menu_items %}
- <li>
- <a href="{{ it.url }}" class="flex items-center justify-between rounded px-3 py-2 hover:bg-gray-50 {% if request.path == it.url %}bg-blue-50 text-blue-700{% else %}text-gray-700{% endif %}">
- <span class="truncate">{{ it.label }}</span>
- </a>
- </li>
- {% endfor %}
- </ul>
- </div>
- </div>
- <div x-data="{ open: {% if '/webadmin/recycle/' in request.path %}true{% else %}false{% endif %} }">
- <div @click="open = !open" class="flex items-center justify-between rounded px-3 py-2 hover:bg-gray-50 cursor-pointer">
- <span class="truncate">{% trans "Recycle Ops" %}</span>
- <svg :class="{'rotate-180': open}" class="w-4 h-4 transform transition-transform" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
- </svg>
- </div>
- <div x-show="open" x-cloak class="pl-4">
- {% app_menu 'recycle_core' as recycle_menu_items %}
- <ul class="space-y-1">
- {% for it in recycle_menu_items %}
- <li>
- <a href="{{ it.url }}" class="flex items-center justify-between rounded px-3 py-2 hover:bg-gray-50 {% if request.path == it.url %}bg-blue-50 text-blue-700{% else %}text-gray-700{% endif %}">
- <span class="truncate">{{ it.label }}</span>
- </a>
- </li>
- {% endfor %}
- </ul>
- </div>
- </div>
- </ul>
- </nav>
- </aside>
- <section class="md:col-span-9">
- {% block content %}{% endblock %}
- </section>
- </div>
- </main>
- <footer class="bg-white border-t border-gray-200">
- <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
- <div class="flex h-16 items-center justify-between">
- <p class="text-sm text-gray-600">© {% now "Y" %} Lux Backend. All rights reserved.</p>
- </div>
- </div>
- </footer>
- </div>
- {% block scripts %}{% endblock %}
- <!-- Flowbite JS (components and interactivity) -->
- <script src="{% static 'flowbite/dist/flowbite.min.js' %}"></script>
- <script defer src="{% static "sortablejs/Sortable.min.js" %}"></script>
- <script>
- // Auto-fade toasts after 5 seconds
- window.addEventListener('DOMContentLoaded', () => {
- document.querySelectorAll('#toasts .toast').forEach((el) => {
- setTimeout(() => {
- el.style.opacity = '0';
- el.style.transition = 'opacity 0.5s ease';
- setTimeout(() => el.remove(), 600);
- }, 5000);
- });
- });
- </script>
- <style>
- /* Ensure system UI controls (date/time pickers) render with dark icons on light background */
- input[type="date"], input[type="time"], input[type="datetime-local"] { color-scheme: light; }
- /* Hide cloaked elements until Alpine initializes */
- [x-cloak] { display: none !important; }
- .success { background-color: #ecfdf5; border-color:#34d399; color:#065f46; }
- .error { background-color: #fef2f2; border-color:#fca5a5; color:#7f1d1d; }
- .info { background-color: #eff6ff; border-color:#93c5fd; color:#1e3a8a; }
- .toast { opacity: 1; }
- #div_id_is_verified .mb-3 { margin-bottom: 0px !important; }
- /* Fallback color for outline buttons when --btn-color is unset */
- </style>
- </body>
- </html>
|