Add organize_enabled toggle for folders and improve UI with loading states
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
**/*.pyc
|
||||
./tmp/**/*
|
||||
|
||||
@@ -31,3 +31,18 @@ is preferred, and this is less preferred:
|
||||
# check if the user is an adult
|
||||
x = age >= 18
|
||||
```
|
||||
|
||||
9. Buttons should be disabled, and a spinner should replace the icon content when loading. Only the button that was clicked should be disabled though. Typically this is done something like this:
|
||||
```
|
||||
<div data-loading-states>
|
||||
<button class="..." hx-get="..." data-loading-disable>
|
||||
<i class="fas fa-cog mr-2" data-loading-class="!hidden"></i>
|
||||
<span data-loading-class="!hidden">Click Me!</span>
|
||||
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span> </button>
|
||||
</div>
|
||||
```
|
||||
This will scope the disable to just the button, and disabled the button from being clicked. When in a modal, the whole modal should be disabled.
|
||||
|
||||
### context7 documentation library ids:
|
||||
* HTMX: /bigskysoftware/htmx
|
||||
* HTMX extensions: /bigskysoftware/htmx-extensions
|
||||
|
||||
15
QWEN.md
15
QWEN.md
@@ -31,3 +31,18 @@ is preferred, and this is less preferred:
|
||||
# check if the user is an adult
|
||||
x = age >= 18
|
||||
```
|
||||
|
||||
9. Buttons should be disabled, and a spinner should replace the icon content when loading. Only the button that was clicked should be disabled though. Typically this is done something like this:
|
||||
```
|
||||
<div data-loading-states>
|
||||
<button class="..." hx-get="..." data-loading-disable>
|
||||
<i class="fas fa-cog mr-2" data-loading-class="!hidden"></i>
|
||||
<span data-loading-class="!hidden">Click Me!</span>
|
||||
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span> </button>
|
||||
</div>
|
||||
```
|
||||
This will scope the disable to just the button, and disabled the button from being clicked. When in a modal, the whole modal should be disabled.
|
||||
|
||||
### context7 documentation library ids:
|
||||
* HTMX: /bigskysoftware/htmx
|
||||
* HTMX extensions: /bigskysoftware/htmx-extensions
|
||||
|
||||
@@ -242,8 +242,9 @@ def imap_config_modal():
|
||||
@login_required
|
||||
def test_imap_connection():
|
||||
"""Test IMAP connection with provided configuration."""
|
||||
print("HELLO")
|
||||
try:
|
||||
import time
|
||||
time.sleep(5)
|
||||
# Get form data
|
||||
server = request.form.get('server')
|
||||
port = request.form.get('port')
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Email Organizer - Prototype{% endblock %}</title>
|
||||
<script src="https://unpkg.com/htmx.org@1.9.12"></script>
|
||||
<script src="https://unpkg.com/htmx-ext-loading-states@2.0.0"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/daisyui@5" rel="stylesheet" type="text/css" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/daisyui@5/themes.css" rel="stylesheet" type="text/css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
@@ -23,7 +24,9 @@
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
<body class="min-h-screen flex flex-col" x-data="{}" x-on:open-modal.window="$refs.modal.showModal()"
|
||||
x-on:close-modal.window="$refs.modal.close()">
|
||||
x-on:close-modal.window="$refs.modal.close()"
|
||||
hx-ext="loading-states"
|
||||
data-loading-delay="200">
|
||||
{% block header %}{% endblock %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
|
||||
@@ -21,39 +21,54 @@
|
||||
<h2 class="text-2xl font-bold">Email Folders</h2>
|
||||
<p class="text-base-content/70">Create and manage your email organization rules</p>
|
||||
</div>
|
||||
<button class="btn btn-primary" hx-get="/api/folders/new" hx-target="#modal-holder"
|
||||
hx-swap="innerHTML">
|
||||
<i class="fas fa-plus mr-2"></i>
|
||||
Add Folder
|
||||
<div data-loading-states>
|
||||
<button class="btn btn-primary" hx-get="/api/folders/new" hx-target="#modal-holder" hx-swap="innerHTML"
|
||||
data-loading-disable>
|
||||
<i class="fas fa-plus mr-2" data-loading-class="!hidden"></i>
|
||||
<span data-loading-class="!hidden">Add Folder</span>
|
||||
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Welcome Section -->
|
||||
<div class="mb-8 p-6 bg-base-100 rounded-box shadow-lg border border-base-300">
|
||||
<h3 class="text-xl font-bold mb-2">Welcome to Email Organizer!</h3>
|
||||
<p class="text-base-content/80 mb-4">Organize your emails automatically with AI-powered rules. Create folders and set up rules to categorize incoming emails.</p>
|
||||
<div class="flex space-x-4">
|
||||
<div class="flex space-x-4" data-loading-states>
|
||||
<button class="btn btn-primary" hx-get="/api/folders/new" hx-target="#modal-holder"
|
||||
hx-swap="innerHTML">
|
||||
<i class="fas fa-plus mr-2"></i>
|
||||
Create Your First Folder
|
||||
hx-swap="innerHTML"
|
||||
data-loading-disable>
|
||||
<i class="fas fa-plus mr-2" data-loading-class="!hidden"></i>
|
||||
<span data-loading-class="!hidden">Create Your First Folder</span>
|
||||
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
||||
</button>
|
||||
{% if current_user.imap_config %}
|
||||
<div data-loading-states>
|
||||
<button class="btn btn-outline" hx-post="/api/imap/sync" hx-target="#folders-list"
|
||||
hx-swap="innerHTML">
|
||||
<i class="fas fa-sync mr-2"></i>
|
||||
Sync Folders
|
||||
hx-swap="innerHTML"
|
||||
data-loading-disable>
|
||||
<i class="fas fa-sync mr-2" data-loading-class="!hidden"></i>
|
||||
<span data-loading-class="!hidden">Sync Folders</span>
|
||||
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
||||
</button>
|
||||
<button class="btn btn-outline" hx-get="/api/imap/config" hx-target="#modal-holder"
|
||||
hx-swap="innerHTML">
|
||||
<i class="fas fa-cog"></i>
|
||||
</div>
|
||||
<div data-loading-states >
|
||||
<button class="btn btn-outline" hx-get="/api/imap/config" hx-target="#modal-holder" hx-swap="innerHTML"
|
||||
data-loading-disable>
|
||||
<i class="fas fa-cog" data-loading-class="!hidden"></i>
|
||||
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
||||
</button>
|
||||
</div>
|
||||
{% else %}
|
||||
<button class="btn btn-outline" hx-get="/api/imap/config" hx-target="#modal-holder"
|
||||
hx-swap="innerHTML">
|
||||
<i class="fas fa-cog mr-2"></i>
|
||||
Configure IMAP
|
||||
<div data-loading-states>
|
||||
<button class="btn btn-outline" hx-get="/api/imap/config" hx-target="#modal-holder" hx-swap="innerHTML"
|
||||
data-loading-disable>
|
||||
<i class="fas fa-cog mr-2" data-loading-class="!hidden"></i>
|
||||
<span data-loading-class="!hidden">Configure IMAP</span>
|
||||
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<div id="folder-{{ folder.id }}" class="card bg-base-100 shadow-xl border border-base-300 hover:shadow-lg transition-shadow duration-200">
|
||||
<div class="card-body">
|
||||
|
||||
<div class="card-body" data-loading-states>
|
||||
<div class="flex justify-between items-start mb-2">
|
||||
<h3 class="text-xl font-bold truncate">{{ folder.name }}</h3>
|
||||
<div class="flex space-x-2">
|
||||
@@ -7,15 +8,21 @@
|
||||
hx-get="/api/folders/{{ folder.id }}/edit"
|
||||
hx-target="#modal-holder"
|
||||
hx-swap="innerHTML"
|
||||
hx-trigger="click">
|
||||
<i class="fas fa-edit"></i>
|
||||
hx-trigger="click"
|
||||
data-loading-disable
|
||||
>
|
||||
<i class="fas fa-edit" data-loading-class="!hidden"></i>
|
||||
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline btn-error"
|
||||
hx-delete="/api/folders/{{ folder.id }}"
|
||||
hx-target="#folders-list"
|
||||
hx-swap="innerHTML"
|
||||
hx-confirm="Are you sure you want to delete this folder?">
|
||||
<i class="fas fa-trash"></i>
|
||||
hx-confirm="Are you sure you want to delete this folder?"
|
||||
data-loading-disable
|
||||
>
|
||||
<i class="fas fa-trash" data-loading-class="!hidden"></i>
|
||||
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -42,8 +49,11 @@
|
||||
hx-target="#folder-{{ folder.id }}"
|
||||
hx-swap="outerHTML"
|
||||
hx-trigger="click"
|
||||
data-loading-disable
|
||||
aria-label="Toggle organize enabled">
|
||||
</input>
|
||||
|
||||
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -42,11 +42,12 @@
|
||||
<option value="-1" {% if (priority is defined and priority == '-1') or (folder and folder.priority==-1) %}selected{% endif %}>Low</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-action">
|
||||
<div class="modal-action" data-loading-states>
|
||||
<button type="button" class="btn btn-outline"
|
||||
@click="$dispatch('close-modal')">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary" id="submit-btn" :class="{ 'shake': errors }">
|
||||
{% if folder %}Update Folder{% else %}Add Folder{% endif %}
|
||||
<button type="submit" class="btn btn-primary" id="submit-btn" :class="{ 'shake': errors }" >
|
||||
<span data-loading-class="!hidden">{% if folder %}Update Folder{% else %}Add Folder{% endif %}</span>
|
||||
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
</div>
|
||||
<h3 class="text-2xl font-bold mb-2">No folders yet</h3>
|
||||
<p class="mb-6 text-base-content/70">Add your first folder to get started organizing your emails.</p>
|
||||
<button class="btn btn-primary btn-lg"
|
||||
hx-get="/api/folders/new"
|
||||
hx-target="#modal-holder"
|
||||
hx-swap="innerHTML"
|
||||
<div data-loading-states>
|
||||
<button class="btn btn-primary btn-lg" hx-get="/api/folders/new" hx-target="#modal-holder" hx-swap="innerHTML"
|
||||
hx-trigger="click">
|
||||
<i class="fas fa-plus mr-2"></i>
|
||||
Create Folder
|
||||
<i class="fas fa-plus mr-2" data-loading-class="!hidden"></i>
|
||||
<span data-loading-class="!hidden">Create Folder</span>
|
||||
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="mt-4 text-sm text-base-content/70">
|
||||
<p>Need help setting up your first folder?</p>
|
||||
<a href="#" class="link link-primary">View tutorial</a>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div id="imap-modal" @click.away="$refs.modal.close()" class="modal-box" x-data="{ errors: {{ 'true' if errors else 'false' }} }" x-init="$nextTick(() => { if (errors) { document.querySelector('#submit-btn').classList.add('shake'); } })">
|
||||
<div id="imap-modal" @click.away="$refs.modal.close()" class="modal-box" x-data="{ errors: {{ 'true' if errors else 'false' }} }" x-init="$nextTick(() => { if (errors) { document.querySelector('#submit-btn').classList.add('shake'); } })" >
|
||||
<h3 class="font-bold text-lg mb-4">Configure IMAP Connection</h3>
|
||||
|
||||
{% if success %}
|
||||
@@ -67,20 +67,21 @@
|
||||
<p class="text-xs text-base-content/70 mt-1">Most IMAP servers use SSL on port 993</p>
|
||||
</div>
|
||||
|
||||
<div class="modal-action">
|
||||
<div class="modal-action" data-loading-states>
|
||||
<button type="button" class="btn btn-outline"
|
||||
@click="$dispatch('close-modal')">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary" id="submit-btn" :class="{ 'shake': errors }">
|
||||
Test Connection
|
||||
<button type="submit" class="btn btn-primary" id="submit-btn" :class="{ 'shake': errors }" data-loading-disable >
|
||||
<span data-loading-class="!hidden">Test Connection</span>
|
||||
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if success %}
|
||||
<div class="mt-4 pt-4 border-t border-base-300">
|
||||
<button class="btn btn-success w-full" hx-post="/api/imap/sync" hx-target="#folders-list" hx-swap="innerHTML">
|
||||
<i class="fas fa-sync mr-2"></i>
|
||||
Sync Folders
|
||||
<div class="mt-4 pt-4 border-t border-base-300" data-loading-states>
|
||||
<button class="btn btn-success w-full" hx-post="/api/imap/sync" hx-target="#folders-list" hx-swap="innerHTML" data-loading-disable>
|
||||
<span data-loading-class="!hidden"><i class="fas fa-sync mr-2"></i>Sync Folders</span>
|
||||
<span class="loading loading-spinner loading-xs hidden" data-loading-class-remove="hidden"></span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!-- Modal Holder -->
|
||||
<dialog id="modal-holder" x-ref="modal" class="modal">
|
||||
<dialog id="modal-holder" x-ref="modal" class="modal" data-loading-states>
|
||||
<!-- Modals will be loaded here via HTMX -->
|
||||
</dialog>
|
||||
Binary file not shown.
Reference in New Issue
Block a user