aboutsummaryrefslogtreecommitdiff
path: root/templates
diff options
context:
space:
mode:
authorBobby <[email protected]>2026-03-08 04:00:38 +0530
committerBobby <[email protected]>2026-03-08 04:00:38 +0530
commit44e056c26936b302478fa4e64e3f8e3e6a9a30cf (patch)
tree15546500b74b10fcb741727440666f2f5167c174 /templates
parentcaf265e7050edefa64ecf7e13828ec9636bce867 (diff)
downloaddove-44e056c26936b302478fa4e64e3f8e3e6a9a30cf.tar.xz
dove-44e056c26936b302478fa4e64e3f8e3e6a9a30cf.zip
feat: Enhance mail and domain management UI and functionality
- Updated users page to include a help link with an icon. - Refactored sidebar navigation to improve organization and added collapsible sections for Domains and Mail. - Created new pages for managing domains and TLDs, including a detailed description of the Domain Manager. - Implemented confirmation modals for deleting TLDs with appropriate messaging. - Added JavaScript functionality for sidebar state management and confirmation modals. - Introduced new Go handlers for mail index and domain management. - Added validation functions for DNS labels and email local parts.
Diffstat (limited to 'templates')
-rw-r--r--templates/domains/htmx/domains.htmx.django45
-rw-r--r--templates/domains/htmx/index.htmx.django32
-rw-r--r--templates/domains/htmx/newdomain.htmx.django28
-rw-r--r--templates/domains/htmx/newtld.htmx.django12
-rw-r--r--templates/domains/htmx/tlds.htmx.django66
-rw-r--r--templates/domains/index.django5
-rw-r--r--templates/domains/tlds.django5
-rw-r--r--templates/layouts/base.django3
-rw-r--r--templates/mail/htmx/index.htmx.django33
-rw-r--r--templates/mail/htmx/mailbox.htmx.django3
-rw-r--r--templates/mail/htmx/mailboxes.htmx.django3
-rw-r--r--templates/mail/htmx/newmailbox.htmx.django32
-rw-r--r--templates/mail/htmx/newuser.htmx.django3
-rw-r--r--templates/mail/htmx/users.htmx.django3
-rw-r--r--templates/mail/index.django5
-rw-r--r--templates/partials/sidebar.django86
16 files changed, 275 insertions, 89 deletions
diff --git a/templates/domains/htmx/domains.htmx.django b/templates/domains/htmx/domains.htmx.django
index 42fd320..bbd8a34 100644
--- a/templates/domains/htmx/domains.htmx.django
+++ b/templates/domains/htmx/domains.htmx.django
@@ -1,51 +1,12 @@
-<h1 id="page-title" class="text-sm font-medium text-zinc-100" hx-swap-oob="true">{{ PageTitle }}</h1>
+{% url "domains.index" as help_path %}
+<h1 id="page-title" class="text-sm font-medium text-zinc-100 flex items-center gap-2" hx-swap-oob="true">{{ PageTitle }}<a href="{{ help_path }}" hx-get="{{ help_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="text-zinc-600 hover:text-zinc-400 transition-colors duration-150"><svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z" /></svg></a></h1>
<div class="slide-up space-y-6">
<div class="glass rounded-xl glow-border">
<div class="flex items-center justify-between px-5 py-4 border-b border-white/[0.04]">
- <h2 class="text-sm font-medium text-zinc-200">TLDs</h2>
- <div class="flex items-center gap-3">
- <span class="text-xs text-zinc-600">{{ tlds|length }} total</span>
- {% url "domains.tlds.new" as new_tld_path %}
- <a href="{{ new_tld_path }}" hx-get="{{ new_tld_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="btn-small">New TLD</a>
- </div>
- </div>
- {% if tlds %}
- <div class="divide-y divide-white/[0.04]">
- {% for tld in tlds %}
- <div class="flex items-center justify-between px-5 py-3">
- <div class="flex items-center gap-3">
- <div class="flex items-center justify-center w-8 h-8 rounded-lg bg-accent-500/10">
- <svg class="w-4 h-4 text-accent-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
- <path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418" />
- </svg>
- </div>
- <div>
- <p class="text-sm text-zinc-200">.{{ tld.Name }}</p>
- {% if tld.IsDefault %}
- <p class="text-xs text-zinc-600">Default</p>
- {% endif %}
- </div>
- </div>
- {% if not tld.IsDefault %}
- {% url "domains.tlds.delete" name=tld.Name as delete_tld_path %}
- <button hx-delete="{{ delete_tld_path }}" hx-confirm="Delete .{{ tld.Name }}?" hx-target="#content" hx-swap="innerHTML" class="text-xs text-zinc-600 hover:text-red-400 transition-colors duration-150">Delete</button>
- {% endif %}
- </div>
- {% endfor %}
- </div>
- {% else %}
- <div class="flex flex-col items-center justify-center py-16 text-center">
- <p class="text-sm text-zinc-400">No TLDs configured</p>
- </div>
- {% endif %}
- </div>
-
- <div class="glass rounded-xl glow-border">
- <div class="flex items-center justify-between px-5 py-4 border-b border-white/[0.04]">
<h2 class="text-sm font-medium text-zinc-200">Domains</h2>
<div class="flex items-center gap-3">
<span class="text-xs text-zinc-600">{{ domains|length }} total</span>
- {% url "domains.new" as new_domain_path %}
+ {% url "domains.manage.new" as new_domain_path %}
<a href="{{ new_domain_path }}" hx-get="{{ new_domain_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="btn-small">New Domain</a>
</div>
</div>
diff --git a/templates/domains/htmx/index.htmx.django b/templates/domains/htmx/index.htmx.django
new file mode 100644
index 0000000..aa96fda
--- /dev/null
+++ b/templates/domains/htmx/index.htmx.django
@@ -0,0 +1,32 @@
+<h1 id="page-title" class="text-sm font-medium text-zinc-100" hx-swap-oob="true">{{ PageTitle }}</h1>
+<div class="slide-up space-y-6">
+ <div class="glass rounded-xl glow-border">
+ <div class="px-6 py-5 border-b border-white/[0.04]">
+ <div class="flex items-center gap-3 mb-4">
+ <div class="flex items-center justify-center w-10 h-10 rounded-xl bg-accent-500/10">
+ <svg class="w-5 h-5 text-accent-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418" />
+ </svg>
+ </div>
+ <h2 class="text-base font-medium text-zinc-100">Domain Manager</h2>
+ </div>
+ <p class="text-sm text-zinc-400 leading-relaxed">The Domain Manager provides local DNS infrastructure for your development environment. It allows you to create custom top-level domains and register domain names that resolve locally, giving your services clean, memorable addresses without modifying your system hosts file.</p>
+ </div>
+ <div class="px-6 py-5 space-y-6">
+ <div>
+ <h3 class="text-sm font-medium text-zinc-200 mb-2">Top-Level Domains (TLDs)</h3>
+ <p class="text-xs text-zinc-500 leading-relaxed mb-2">TLDs are the root of your local domain hierarchy. Dove ships with default TLDs like <span class="text-zinc-400">local</span> and <span class="text-zinc-400">dev</span>, and you can create custom ones to match your organisation or project structure.</p>
+ <p class="text-xs text-zinc-500 leading-relaxed">Default TLDs are protected and cannot be deleted. Custom TLDs can only be removed once all domains registered under them have been deleted first.</p>
+ </div>
+ <div class="border-t border-white/[0.04] pt-5">
+ <h3 class="text-sm font-medium text-zinc-200 mb-2">Domains</h3>
+ <p class="text-xs text-zinc-500 leading-relaxed mb-2">Domains are registered under a TLD and serve as the address for your local services. For example, registering <span class="text-zinc-400">myapp</span> under <span class="text-zinc-400">local</span> gives you <span class="text-zinc-400">myapp.local</span>.</p>
+ <p class="text-xs text-zinc-500 leading-relaxed">Each domain can be mapped to local services through DNS A records with port support, similar to how Cloudflare handles traffic routing. This means your services get proper domain names that resolve to the correct local ports.</p>
+ </div>
+ <div class="border-t border-white/[0.04] pt-5">
+ <h3 class="text-sm font-medium text-zinc-200 mb-2">How It Works</h3>
+ <p class="text-xs text-zinc-500 leading-relaxed">Dove runs a local DNS server that intercepts queries for your registered TLDs and returns the appropriate local addresses. External domains continue to resolve normally through your upstream DNS. No system configuration changes are required beyond pointing your DNS resolver at Dove.</p>
+ </div>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/templates/domains/htmx/newdomain.htmx.django b/templates/domains/htmx/newdomain.htmx.django
index 5f188c1..9f0f20a 100644
--- a/templates/domains/htmx/newdomain.htmx.django
+++ b/templates/domains/htmx/newdomain.htmx.django
@@ -1,11 +1,12 @@
-<h1 id="page-title" class="text-sm font-medium text-zinc-100" hx-swap-oob="true">{{ PageTitle }}</h1>
+{% url "domains.index" as help_path %}
+<h1 id="page-title" class="text-sm font-medium text-zinc-100 flex items-center gap-2" hx-swap-oob="true">{{ PageTitle }}<a href="{{ help_path }}" hx-get="{{ help_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="text-zinc-600 hover:text-zinc-400 transition-colors duration-150"><svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z" /></svg></a></h1>
<div class="slide-up flex items-start justify-center pt-12">
<div class="glass rounded-xl glow-border w-full max-w-lg">
<div class="px-5 py-4 border-b border-white/[0.04]">
<h2 class="text-sm font-medium text-zinc-200">Register Domain</h2>
</div>
<div class="p-5">
- {% url "domains.create" as create_path %}
+ {% url "domains.manage.create" as create_path %}
<form method="POST" action="{{ create_path }}" class="space-y-4">
<div>
<label class="block text-xs font-medium text-zinc-400 mb-1.5 ml-1">Domain Name</label>
@@ -13,27 +14,34 @@
</div>
<div>
<label class="block text-xs font-medium text-zinc-400 mb-1.5 ml-1">TLD</label>
- <div data-dropdown>
+ <div class="dropdown" data-dropdown>
<input type="hidden" name="tld_name" data-dropdown-value>
<button type="button" data-dropdown-trigger class="input-field text-left flex items-center justify-between">
- <span data-dropdown-label class="text-zinc-500">Select a TLD</span>
- <svg class="w-4 h-4 text-zinc-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
+ <span class="truncate" data-dropdown-label>Select a TLD</span>
+ <svg class="w-4 h-4 text-zinc-500 shrink-0 ml-2 transition-transform duration-150" data-dropdown-chevron fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
</button>
- <div data-dropdown-menu class="dropdown-menu">
- <input type="text" data-dropdown-search class="dropdown-search" placeholder="Search TLDs...">
- <div data-dropdown-options class="dropdown-options">
+ <div class="dropdown-menu" data-dropdown-menu>
+ <div class="p-2 border-b border-white/[0.04]">
+ <input type="text" placeholder="Search TLDs..." class="dropdown-search" data-dropdown-search>
+ </div>
+ <div class="dropdown-options" data-dropdown-options>
{% for tld in tlds %}
- <div data-dropdown-option data-value="{{ tld.Name }}" class="dropdown-option">.{{ tld.Name }}</div>
+ <button type="button" class="dropdown-option" data-dropdown-option data-value="{{ tld.Name }}" data-label=".{{ tld.Name }}">
+ <p class="text-sm text-zinc-200">.{{ tld.Name }}</p>
+ </button>
{% endfor %}
</div>
+ <div class="dropdown-empty hidden" data-dropdown-empty>
+ <p class="text-xs text-zinc-500 text-center py-3">No TLDs found</p>
+ </div>
</div>
</div>
</div>
<div class="flex items-center gap-3 pt-2">
<button type="submit" class="btn-primary">Register Domain</button>
- {% url "domains.index" as domains_path %}
+ {% url "domains.manage" as domains_path %}
<a href="{{ domains_path }}" hx-get="{{ domains_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="text-xs text-zinc-500 hover:text-zinc-300 transition-colors duration-150">Cancel</a>
</div>
</form>
diff --git a/templates/domains/htmx/newtld.htmx.django b/templates/domains/htmx/newtld.htmx.django
index 39d09d2..9de64e1 100644
--- a/templates/domains/htmx/newtld.htmx.django
+++ b/templates/domains/htmx/newtld.htmx.django
@@ -1,4 +1,5 @@
-<h1 id="page-title" class="text-sm font-medium text-zinc-100" hx-swap-oob="true">{{ PageTitle }}</h1>
+{% url "domains.index" as help_path %}
+<h1 id="page-title" class="text-sm font-medium text-zinc-100 flex items-center gap-2" hx-swap-oob="true">{{ PageTitle }}<a href="{{ help_path }}" hx-get="{{ help_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="text-zinc-600 hover:text-zinc-400 transition-colors duration-150"><svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z" /></svg></a></h1>
<div class="slide-up flex items-start justify-center pt-12">
<div class="glass rounded-xl glow-border w-full max-w-lg">
<div class="px-5 py-4 border-b border-white/[0.04]">
@@ -9,15 +10,12 @@
<form method="POST" action="{{ create_path }}" class="space-y-4">
<div>
<label class="block text-xs font-medium text-zinc-400 mb-1.5 ml-1">TLD Name</label>
- <div class="flex items-center gap-2">
- <span class="text-sm text-zinc-500">.</span>
- <input type="text" name="name" required autocomplete="off" placeholder="example" class="input-field">
- </div>
+ <input type="text" name="name" required autocomplete="off" placeholder="example" class="input-field">
</div>
<div class="flex items-center gap-3 pt-2">
<button type="submit" class="btn-primary">Create TLD</button>
- {% url "domains.index" as domains_path %}
- <a href="{{ domains_path }}" hx-get="{{ domains_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="text-xs text-zinc-500 hover:text-zinc-300 transition-colors duration-150">Cancel</a>
+ {% url "domains.tlds" as tlds_path %}
+ <a href="{{ tlds_path }}" hx-get="{{ tlds_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="text-xs text-zinc-500 hover:text-zinc-300 transition-colors duration-150">Cancel</a>
</div>
</form>
</div>
diff --git a/templates/domains/htmx/tlds.htmx.django b/templates/domains/htmx/tlds.htmx.django
new file mode 100644
index 0000000..8290d41
--- /dev/null
+++ b/templates/domains/htmx/tlds.htmx.django
@@ -0,0 +1,66 @@
+{% url "domains.index" as help_path %}
+<h1 id="page-title" class="text-sm font-medium text-zinc-100 flex items-center gap-2" hx-swap-oob="true">{{ PageTitle }}<a href="{{ help_path }}" hx-get="{{ help_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="text-zinc-600 hover:text-zinc-400 transition-colors duration-150"><svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z" /></svg></a></h1>
+<div class="slide-up space-y-6">
+ <div class="glass rounded-xl glow-border">
+ <div class="flex items-center justify-between px-5 py-4 border-b border-white/[0.04]">
+ <h2 class="text-sm font-medium text-zinc-200">TLDs</h2>
+ <div class="flex items-center gap-3">
+ <span class="text-xs text-zinc-600">{{ tlds|length }} total</span>
+ {% url "domains.tlds.new" as new_tld_path %}
+ <a href="{{ new_tld_path }}" hx-get="{{ new_tld_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="btn-small">New TLD</a>
+ </div>
+ </div>
+ {% if tlds %}
+ <div class="divide-y divide-white/[0.04]">
+ {% for tld in tlds %}
+ <div class="flex items-center justify-between px-5 py-3">
+ <div class="flex items-center gap-3">
+ <div class="flex items-center justify-center w-8 h-8 rounded-lg bg-accent-500/10">
+ <svg class="w-4 h-4 text-accent-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418" />
+ </svg>
+ </div>
+ <div>
+ <p class="text-sm text-zinc-200">.{{ tld.Name }}</p>
+ {% if tld.IsDefault %}
+ <p class="text-xs text-zinc-600">Default</p>
+ {% else %}
+ <p class="text-xs text-zinc-600">Custom</p>
+ {% endif %}
+ </div>
+ </div>
+ {% if not tld.IsDefault %}
+ {% url "domains.tlds.delete" name=tld.Name as delete_tld_path %}
+ <button data-confirm-trigger data-confirm-title="Delete .{{ tld.Name }}" data-confirm-message="This TLD and all associated data will be permanently removed. This action cannot be undone." data-confirm-action="{{ delete_tld_path }}" class="text-xs text-zinc-600 hover:text-red-400 transition-colors duration-150">Delete</button>
+ {% endif %}
+ </div>
+ {% endfor %}
+ </div>
+ {% else %}
+ <div class="flex flex-col items-center justify-center py-16 text-center">
+ <p class="text-sm text-zinc-400">No TLDs configured</p>
+ </div>
+ {% endif %}
+ </div>
+
+ <div id="confirm-modal" class="fixed inset-0 z-50 hidden">
+ <div class="absolute inset-0 bg-black/60 backdrop-blur-sm" data-confirm-backdrop></div>
+ <div class="flex items-center justify-center min-h-screen p-4">
+ <div class="relative glass rounded-xl glow-border w-full max-w-sm p-6 space-y-4">
+ <div class="flex items-center gap-3">
+ <div class="flex items-center justify-center w-10 h-10 rounded-xl bg-red-500/10">
+ <svg class="w-5 h-5 text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z" />
+ </svg>
+ </div>
+ <h3 id="confirm-title" class="text-sm font-medium text-zinc-100"></h3>
+ </div>
+ <p id="confirm-message" class="text-xs text-zinc-400 leading-relaxed"></p>
+ <div class="flex items-center justify-end gap-3 pt-2">
+ <button data-confirm-cancel class="px-4 py-2 text-xs text-zinc-400 hover:text-zinc-200 rounded-lg bg-surface-800 border border-white/[0.06] hover:border-white/[0.1] transition-all duration-150">Cancel</button>
+ <button id="confirm-action" class="px-4 py-2 text-xs text-white rounded-lg bg-red-500/80 hover:bg-red-500 border border-red-400/20 transition-all duration-150">Delete</button>
+ </div>
+ </div>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/templates/domains/index.django b/templates/domains/index.django
new file mode 100644
index 0000000..2bd644f
--- /dev/null
+++ b/templates/domains/index.django
@@ -0,0 +1,5 @@
+{% extends "layouts/dashboard.django" %}
+
+{% block dashboard %}
+{% include "domains/htmx/index.htmx.django" %}
+{% endblock %} \ No newline at end of file
diff --git a/templates/domains/tlds.django b/templates/domains/tlds.django
new file mode 100644
index 0000000..efc4289
--- /dev/null
+++ b/templates/domains/tlds.django
@@ -0,0 +1,5 @@
+{% extends "layouts/dashboard.django" %}
+
+{% block dashboard %}
+{% include "domains/htmx/tlds.htmx.django" %}
+{% endblock %} \ No newline at end of file
diff --git a/templates/layouts/base.django b/templates/layouts/base.django
index bcfab20..c858bce 100644
--- a/templates/layouts/base.django
+++ b/templates/layouts/base.django
@@ -15,8 +15,9 @@
</div>
{% block content %}{% endblock %}
<script src="/static/js/htmx.min.js"></script>
- <script src="/static/js/navigation.js"></script>
+ <script src="/static/js/sidebar.js"></script>
<script src="/static/js/dropdown.js"></script>
+ <script src="/static/js/confirm.js"></script>
{% block scripts %}{% endblock %}
</body>
</html>
diff --git a/templates/mail/htmx/index.htmx.django b/templates/mail/htmx/index.htmx.django
new file mode 100644
index 0000000..abbd800
--- /dev/null
+++ b/templates/mail/htmx/index.htmx.django
@@ -0,0 +1,33 @@
+<h1 id="page-title" class="text-sm font-medium text-zinc-100" hx-swap-oob="true">{{ PageTitle }}</h1>
+<div class="slide-up space-y-6">
+ <div class="glass rounded-xl glow-border">
+ <div class="px-6 py-5 border-b border-white/[0.04]">
+ <div class="flex items-center gap-3 mb-4">
+ <div class="flex items-center justify-center w-10 h-10 rounded-xl bg-accent-500/10">
+ <svg class="w-5 h-5 text-accent-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
+ <path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0v.243a2.25 2.25 0 0 1-1.07 1.916l-7.5 4.615a2.25 2.25 0 0 1-2.36 0L3.32 8.91a2.25 2.25 0 0 1-1.07-1.916V6.75" />
+ </svg>
+ </div>
+ <h2 class="text-base font-medium text-zinc-100">Mail</h2>
+ </div>
+ <p class="text-sm text-zinc-400 leading-relaxed">Dove provides a complete local mail infrastructure with full SMTP, IMAP, and POP3 support. Create users, assign mailboxes, and send and receive emails across your registered domains, all running locally with zero external dependencies.</p>
+ </div>
+ <div class="px-6 py-5 space-y-6">
+ <div>
+ <h3 class="text-sm font-medium text-zinc-200 mb-2">Users</h3>
+ <p class="text-xs text-zinc-500 leading-relaxed mb-2">Mail users are the identities that own mailboxes and authenticate against the mail server. Each user has a username and display name, and can be assigned one or more mailboxes.</p>
+ <p class="text-xs text-zinc-500 leading-relaxed">Users authenticate via SMTP to send emails and via IMAP or POP3 to retrieve them. A single user can manage multiple mailboxes across different domains.</p>
+ </div>
+ <div class="border-t border-white/[0.04] pt-5">
+ <h3 class="text-sm font-medium text-zinc-200 mb-2">Mailboxes</h3>
+ <p class="text-xs text-zinc-500 leading-relaxed mb-2">Mailboxes are email addresses assigned to users on your registered domains. For example, a user can own <span class="text-zinc-400">[email protected]</span> or <span class="text-zinc-400">[email protected]</span>.</p>
+ <p class="text-xs text-zinc-500 leading-relaxed">Each mailbox stores incoming and outgoing emails independently. Mailboxes are tied to domains managed through the Domain Manager, so you must have at least one domain registered before creating a mailbox.</p>
+ </div>
+ <div class="border-t border-white/[0.04] pt-5">
+ <h3 class="text-sm font-medium text-zinc-200 mb-2">How It Works</h3>
+ <p class="text-xs text-zinc-500 leading-relaxed mb-2">Dove runs a full-featured SMTP server that handles both sending and receiving emails locally. Any application on your machine can connect to the SMTP server to send emails, and users can retrieve their mail through IMAP or POP3.</p>
+ <p class="text-xs text-zinc-500 leading-relaxed">All mail is stored locally in Dove's database. This gives you a complete email workflow for development and testing without relying on external mail services or sandbox APIs.</p>
+ </div>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/templates/mail/htmx/mailbox.htmx.django b/templates/mail/htmx/mailbox.htmx.django
index 831a07d..4a10b26 100644
--- a/templates/mail/htmx/mailbox.htmx.django
+++ b/templates/mail/htmx/mailbox.htmx.django
@@ -1,4 +1,5 @@
-<h1 id="page-title" class="text-sm font-medium text-zinc-100" hx-swap-oob="true">{{ PageTitle }}</h1>
+{% url "mail.index" as help_path %}
+<h1 id="page-title" class="text-sm font-medium text-zinc-100 flex items-center gap-2" hx-swap-oob="true">{{ PageTitle }}<a href="{{ help_path }}" hx-get="{{ help_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="text-zinc-600 hover:text-zinc-400 transition-colors duration-150"><svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z" /></svg></a></h1>
<div class="slide-up space-y-6">
<div class="glass rounded-xl glow-border">
<div class="flex items-center justify-between px-5 py-4 border-b border-white/[0.04]">
diff --git a/templates/mail/htmx/mailboxes.htmx.django b/templates/mail/htmx/mailboxes.htmx.django
index 96094f3..e2488c4 100644
--- a/templates/mail/htmx/mailboxes.htmx.django
+++ b/templates/mail/htmx/mailboxes.htmx.django
@@ -1,4 +1,5 @@
-<h1 id="page-title" class="text-sm font-medium text-zinc-100" hx-swap-oob="true">{{ PageTitle }}</h1>
+{% url "mail.index" as help_path %}
+<h1 id="page-title" class="text-sm font-medium text-zinc-100 flex items-center gap-2" hx-swap-oob="true">{{ PageTitle }}<a href="{{ help_path }}" hx-get="{{ help_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="text-zinc-600 hover:text-zinc-400 transition-colors duration-150"><svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z" /></svg></a></h1>
<div class="slide-up space-y-6">
<div class="glass rounded-xl glow-border">
<div class="flex items-center justify-between px-5 py-4 border-b border-white/[0.04]">
diff --git a/templates/mail/htmx/newmailbox.htmx.django b/templates/mail/htmx/newmailbox.htmx.django
index 0962bea..ed886a4 100644
--- a/templates/mail/htmx/newmailbox.htmx.django
+++ b/templates/mail/htmx/newmailbox.htmx.django
@@ -1,4 +1,5 @@
-<h1 id="page-title" class="text-sm font-medium text-zinc-100" hx-swap-oob="true">{{ PageTitle }}</h1>
+{% url "mail.index" as help_path %}
+<h1 id="page-title" class="text-sm font-medium text-zinc-100 flex items-center gap-2" hx-swap-oob="true">{{ PageTitle }}<a href="{{ help_path }}" hx-get="{{ help_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="text-zinc-600 hover:text-zinc-400 transition-colors duration-150"><svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z" /></svg></a></h1>
<div class="slide-up flex items-start justify-center pt-12">
<div class="glass rounded-xl glow-border w-full max-w-lg">
<div class="px-5 py-4 border-b border-white/[0.04]">
@@ -9,7 +10,34 @@
<form method="POST" action="{{ create_path }}" class="space-y-4">
<div>
<label class="block text-xs font-medium text-zinc-400 mb-1.5 ml-1">Address</label>
- <input type="email" name="address" required autocomplete="off" class="input-field">
+ <div class="flex items-center gap-2">
+ <input type="text" name="local_part" required autocomplete="off" placeholder="info" class="input-field flex-1">
+ <span class="text-sm text-zinc-500">@</span>
+ <div class="dropdown flex-1" data-dropdown>
+ <input type="hidden" name="domain_id" data-dropdown-value>
+ <button type="button" class="input-field text-left flex items-center justify-between" data-dropdown-trigger>
+ <span class="truncate" data-dropdown-label>Select a domain</span>
+ <svg class="w-4 h-4 text-zinc-500 shrink-0 ml-2 transition-transform duration-150" data-dropdown-chevron fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
+ <path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
+ </svg>
+ </button>
+ <div class="dropdown-menu" data-dropdown-menu>
+ <div class="p-2 border-b border-white/[0.04]">
+ <input type="text" placeholder="Search domains..." class="dropdown-search" data-dropdown-search>
+ </div>
+ <div class="dropdown-options" data-dropdown-options>
+ {% for domain in domains %}
+ <button type="button" class="dropdown-option" data-dropdown-option data-value="{{ domain.ID }}" data-label="{{ domain.Name }}.{{ domain.TLD.Name }}">
+ <p class="text-sm text-zinc-200">{{ domain.Name }}.{{ domain.TLD.Name }}</p>
+ </button>
+ {% endfor %}
+ </div>
+ <div class="dropdown-empty hidden" data-dropdown-empty>
+ <p class="text-xs text-zinc-500 text-center py-3">No domains found</p>
+ </div>
+ </div>
+ </div>
+ </div>
</div>
<div>
<label class="block text-xs font-medium text-zinc-400 mb-1.5 ml-1">Owner</label>
diff --git a/templates/mail/htmx/newuser.htmx.django b/templates/mail/htmx/newuser.htmx.django
index c6d4d10..ab9764d 100644
--- a/templates/mail/htmx/newuser.htmx.django
+++ b/templates/mail/htmx/newuser.htmx.django
@@ -1,4 +1,5 @@
-<h1 id="page-title" class="text-sm font-medium text-zinc-100" hx-swap-oob="true">{{ PageTitle }}</h1>
+{% url "mail.index" as help_path %}
+<h1 id="page-title" class="text-sm font-medium text-zinc-100 flex items-center gap-2" hx-swap-oob="true">{{ PageTitle }}<a href="{{ help_path }}" hx-get="{{ help_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="text-zinc-600 hover:text-zinc-400 transition-colors duration-150"><svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z" /></svg></a></h1>
<div class="slide-up flex items-start justify-center pt-12">
<div class="glass rounded-xl glow-border w-full max-w-lg">
<div class="px-5 py-4 border-b border-white/[0.04]">
diff --git a/templates/mail/htmx/users.htmx.django b/templates/mail/htmx/users.htmx.django
index 7ec040c..9724391 100644
--- a/templates/mail/htmx/users.htmx.django
+++ b/templates/mail/htmx/users.htmx.django
@@ -1,4 +1,5 @@
-<h1 id="page-title" class="text-sm font-medium text-zinc-100" hx-swap-oob="true">{{ PageTitle }}</h1>
+{% url "mail.index" as help_path %}
+<h1 id="page-title" class="text-sm font-medium text-zinc-100 flex items-center gap-2" hx-swap-oob="true">{{ PageTitle }}<a href="{{ help_path }}" hx-get="{{ help_path }}" hx-target="#content" hx-swap="innerHTML" hx-push-url="true" class="text-zinc-600 hover:text-zinc-400 transition-colors duration-150"><svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z" /></svg></a></h1>
<div class="slide-up space-y-6">
<div class="glass rounded-xl glow-border">
<div class="flex items-center justify-between px-5 py-4 border-b border-white/[0.04]">
diff --git a/templates/mail/index.django b/templates/mail/index.django
new file mode 100644
index 0000000..af123ab
--- /dev/null
+++ b/templates/mail/index.django
@@ -0,0 +1,5 @@
+{% extends "layouts/dashboard.django" %}
+
+{% block dashboard %}
+{% include "mail/htmx/index.htmx.django" %}
+{% endblock %} \ No newline at end of file
diff --git a/templates/partials/sidebar.django b/templates/partials/sidebar.django
index 0530bb7..adf0b28 100644
--- a/templates/partials/sidebar.django
+++ b/templates/partials/sidebar.django
@@ -8,35 +8,75 @@
<span class="text-sm font-semibold text-zinc-100 tracking-tight">Dove</span>
</div>
- <nav class="flex flex-col gap-1 p-3" id="sidebar-nav" hx-target="#content" hx-swap="innerHTML" hx-push-url="true">
+ <nav class="flex flex-col gap-0.5 p-3" id="sidebar-nav" hx-target="#content" hx-swap="innerHTML" hx-push-url="true">
{% url "dashboard.index" as overview_path %}
- {% url "domains.index" as domains_path %}
- {% url "mail.mailboxes" as mailboxes_path %}
+ {% url "domains.tlds" as tlds_path %}
+ {% url "domains.manage" as manage_path %}
{% url "mail.users" as users_path %}
- <a href="{{ overview_path }}" hx-get="{{ overview_path }}" class="nav-link flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors duration-150 {% if Request.Path == overview_path %}active{% endif %}">
+ {% url "mail.mailboxes" as mailboxes_path %}
+
+ <a href="{{ overview_path }}" hx-get="{{ overview_path }}" class="nav-link flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors duration-150">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6A2.25 2.25 0 0 1 6 3.75h2.25A2.25 2.25 0 0 1 10.5 6v2.25a2.25 2.25 0 0 1-2.25 2.25H6a2.25 2.25 0 0 1-2.25-2.25V6ZM3.75 15.75A2.25 2.25 0 0 1 6 13.5h2.25a2.25 2.25 0 0 1 2.25 2.25V18a2.25 2.25 0 0 1-2.25 2.25H6A2.25 2.25 0 0 1 3.75 18v-2.25ZM13.5 6a2.25 2.25 0 0 1 2.25-2.25H18A2.25 2.25 0 0 1 20.25 6v2.25A2.25 2.25 0 0 1 18 10.5h-2.25a2.25 2.25 0 0 1-2.25-2.25V6ZM13.5 15.75a2.25 2.25 0 0 1 2.25-2.25H18a2.25 2.25 0 0 1 2.25 2.25V18A2.25 2.25 0 0 1 18 20.25h-2.25a2.25 2.25 0 0 1-2.25-2.25v-2.25Z" />
</svg>
Overview
</a>
- <a href="{{ domains_path }}" hx-get="{{ domains_path }}" class="nav-link flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors duration-150 {% if Request.Path == domains_path %}active{% endif %}">
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
- <path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418" />
- </svg>
- Domains
- </a>
- <a href="{{ mailboxes_path }}" hx-get="{{ mailboxes_path }}" class="nav-link flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors duration-150 {% if Request.Path == mailboxes_path %}active{% endif %}">
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
- <path stroke-linecap="round" stroke-linejoin="round" d="M2.25 13.5h3.86a2.25 2.25 0 0 1 2.012 1.244l.256.512a2.25 2.25 0 0 0 2.013 1.244h2.21a2.25 2.25 0 0 0 2.013-1.244l.256-.512a2.25 2.25 0 0 1 2.013-1.244h3.859m-19.5.338V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18v-4.162c0-.224-.034-.447-.1-.661L19.24 5.338a2.25 2.25 0 0 0-2.15-1.588H6.911a2.25 2.25 0 0 0-2.15 1.588L2.35 13.177a2.25 2.25 0 0 0-.1.661Z" />
- </svg>
- Mailboxes
- </a>
- <a href="{{ users_path }}" hx-get="{{ users_path }}" class="nav-link flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors duration-150 {% if Request.Path == users_path %}active{% endif %}">
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
- <path stroke-linecap="round" stroke-linejoin="round" d="M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z" />
- </svg>
- Users
- </a>
+
+ <div data-sidebar-section data-section-prefix="/domains">
+ <button data-sidebar-trigger class="nav-link flex items-center justify-between w-full px-3 py-2 rounded-lg text-sm transition-colors duration-150">
+ <span class="flex items-center gap-3">
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418" />
+ </svg>
+ Domain Manager
+ </span>
+ <svg class="w-3.5 h-3.5 text-zinc-600 transition-transform duration-150" data-sidebar-chevron fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
+ <path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
+ </svg>
+ </button>
+ <div data-sidebar-children class="mt-0.5 flex flex-col gap-0.5 pl-3 hidden">
+ <a href="{{ tlds_path }}" hx-get="{{ tlds_path }}" class="nav-link flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors duration-150">
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418" />
+ </svg>
+ TLDs
+ </a>
+ <a href="{{ manage_path }}" hx-get="{{ manage_path }}" class="nav-link flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors duration-150">
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
+ <path stroke-linecap="round" stroke-linejoin="round" d="M5.25 14.25h13.5m-13.5 0a3 3 0 0 1-3-3m3 3a3 3 0 1 0 0 6h13.5a3 3 0 1 0 0-6m-16.5-3a3 3 0 0 1 3-3h13.5a3 3 0 0 1 3 3m-19.5 0a4.5 4.5 0 0 1 .9-2.7L5.737 5.1a3.375 3.375 0 0 1 2.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 0 1 .9 2.7m0 0a3 3 0 0 1-3 3m0 3h.008v.008h-.008v-.008Zm0-6h.008v.008h-.008v-.008Zm-3 6h.008v.008h-.008v-.008Zm0-6h.008v.008h-.008v-.008Z" />
+ </svg>
+ Domains
+ </a>
+ </div>
+ </div>
+
+ <div data-sidebar-section data-section-prefix="/mail">
+ <button data-sidebar-trigger class="nav-link flex items-center justify-between w-full px-3 py-2 rounded-lg text-sm transition-colors duration-150">
+ <span class="flex items-center gap-3">
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
+ <path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0v.243a2.25 2.25 0 0 1-1.07 1.916l-7.5 4.615a2.25 2.25 0 0 1-2.36 0L3.32 8.91a2.25 2.25 0 0 1-1.07-1.916V6.75" />
+ </svg>
+ Mail
+ </span>
+ <svg class="w-3.5 h-3.5 text-zinc-600 transition-transform duration-150" data-sidebar-chevron fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
+ <path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5" />
+ </svg>
+ </button>
+ <div data-sidebar-children class="mt-0.5 flex flex-col gap-0.5 pl-3 hidden">
+ <a href="{{ users_path }}" hx-get="{{ users_path }}" class="nav-link flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors duration-150">
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
+ <path stroke-linecap="round" stroke-linejoin="round" d="M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z" />
+ </svg>
+ Users
+ </a>
+ <a href="{{ mailboxes_path }}" hx-get="{{ mailboxes_path }}" class="nav-link flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-colors duration-150">
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
+ <path stroke-linecap="round" stroke-linejoin="round" d="M2.25 13.5h3.86a2.25 2.25 0 0 1 2.012 1.244l.256.512a2.25 2.25 0 0 0 2.013 1.244h2.21a2.25 2.25 0 0 0 2.013-1.244l.256-.512a2.25 2.25 0 0 1 2.013-1.244h3.859m-19.5.338V18a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18v-4.162c0-.224-.034-.447-.1-.661L19.24 5.338a2.25 2.25 0 0 0-2.15-1.588H6.911a2.25 2.25 0 0 0-2.15 1.588L2.35 13.177a2.25 2.25 0 0 0-.1.661Z" />
+ </svg>
+ Mailboxes
+ </a>
+ </div>
+ </div>
</nav>
<div class="mt-auto p-3 border-t border-white/[0.04]">
@@ -53,4 +93,4 @@
</a>
{% endif %}
</div>
-</aside>
+</aside> \ No newline at end of file