support toggle.

This commit is contained in:
Bryce
2025-08-05 07:32:56 -07:00
parent 9769b03c0b
commit 1eca7f3ff9
15 changed files with 177 additions and 41 deletions

View File

@@ -39,5 +39,6 @@ class Folder(Base):
name = db.Column(db.String(255), nullable=False)
rule_text = db.Column(db.Text)
priority = db.Column(db.Integer)
organize_enabled = db.Column(db.Boolean, default=True)
user = db.relationship('User', backref=db.backref('folders', lazy=True))

View File

@@ -116,6 +116,30 @@ def delete_folder(folder_id):
folders = Folder.query.filter_by(user_id=current_user.id).all()
return render_template('partials/folders_list.html', folders=folders)
@main.route('/api/folders/<folder_id>/toggle', methods=['PUT'])
@login_required
def toggle_folder_organize(folder_id):
try:
# Find the folder by ID and ensure it belongs to the current user
folder = Folder.query.filter_by(id=folder_id, user_id=current_user.id).first()
if not folder:
return jsonify({'error': 'Folder not found'}), 404
# Toggle the organize_enabled flag
folder.organize_enabled = not folder.organize_enabled
db.session.commit()
# Return just the updated folder card HTML for this specific folder
return render_template('partials/folder_card.html', folder=folder)
except Exception as e:
# Print unhandled exceptions to the console as required
logging.exception("Error toggling folder organize flag: %s", e)
db.session.rollback()
return jsonify({'error': 'An unexpected error occurred'}), 500
@main.route('/api/folders/<folder_id>/edit', methods=['GET'])
@login_required
def edit_folder_modal(folder_id):

View File

@@ -0,0 +1,50 @@
<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="flex justify-between items-start mb-2">
<h3 class="text-xl font-bold truncate">{{ folder.name }}</h3>
<div class="flex space-x-2">
<button class="btn btn-sm btn-outline"
hx-get="/api/folders/{{ folder.id }}/edit"
hx-target="#modal-holder"
hx-swap="innerHTML"
hx-trigger="click">
<i class="fas fa-edit"></i>
</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>
</button>
</div>
</div>
<div class="bg-base-200 rounded-box p-4 mb-4">
<p class="text-base-content/80">{{ folder.rule_text }}</p>
</div>
<div class="flex justify-between items-center mt-2">
{% if folder.priority == 1 %}
<span class="badge badge-error">High Priority</span>
{% elif folder.priority == -1 %}
<span class="badge badge-info">Low Priority</span>
{% else %}
<span class="badge badge-primary">Normal Priority</span>
{% endif %}
<div class="flex items-center space-x-2">
<span class="text-xs">Organize:</span>
<input
type="checkbox"
class="toggle toggle-sm toggle-success"
{% if folder.organize_enabled %}checked="checked"{% endif %}
hx-put="/api/folders/{{ folder.id }}/toggle"
hx-target="#folder-{{ folder.id }}"
hx-swap="outerHTML"
hx-trigger="click"
aria-label="Toggle organize enabled">
</input>
</div>
</div>
</div>
</div>

View File

@@ -1,43 +1,6 @@
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div id="folders-list" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{% for folder in folders %}
<div class="card bg-base-100 shadow-xl border border-base-300 hover:shadow-lg transition-shadow duration-200">
<div class="card-body">
<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">
<button class="btn btn-sm btn-outline"
hx-get="/api/folders/{{ folder.id }}/edit"
hx-target="#modal-holder"
hx-swap="innerHTML"
hx-trigger="click">
<i class="fas fa-edit"></i>
</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>
</button>
</div>
</div>
<div class="bg-base-200 rounded-box p-4 mb-4">
<p class="text-base-content/80">{{ folder.rule_text }}</p>
</div>
<div class="flex justify-between items-center mt-2">
{% if folder.priority == 1 %}
<span class="badge badge-error">High Priority</span>
{% elif folder.priority == -1 %}
<span class="badge badge-info">Low Priority</span>
{% else %}
<span class="badge badge-primary">Normal Priority</span>
{% endif %}
<span class="text-xs badge badge-secondary">0 emails</span>
</div>
</div>
</div>
{% include 'partials/folder_card.html' %}
{% else %}
<div class="col-span-full text-center py-12 bg-base-100 rounded-box shadow-lg border border-dashed border-base-300">
<div class="text-5xl mb-4 text-primary">

View File

@@ -0,0 +1,32 @@
"""adding toggle
Revision ID: f8ba65458ba2
Revises: 0bcf54a7f2a7
Create Date: 2025-08-05 06:58:59.265593
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'f8ba65458ba2'
down_revision = '0bcf54a7f2a7'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('folders', schema=None) as batch_op:
batch_op.add_column(sa.Column('organize_enabled', sa.Boolean(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('folders', schema=None) as batch_op:
batch_op.drop_column('organize_enabled')
# ### end Alembic commands ###

View File

@@ -1,5 +1,6 @@
import pytest
from app.models import User, Folder
from app import db
import uuid
from bs4 import BeautifulSoup
@@ -309,3 +310,63 @@ def test_edit_folder_content_whitespace_handling(authenticated_client, mock_fold
assert updated_folder.name == 'Updated Folder With Whitespace' # Should be trimmed
assert updated_folder.rule_text == 'Updated rule with whitespace around it' # Should be trimmed
assert updated_folder.priority == int(test_priority)
def test_toggle_folder_organize_enabled(authenticated_client, mock_folder):
"""Test toggling the organize_enabled flag for a folder."""
# Verify initial state is True (default)
assert mock_folder.organize_enabled is True
# Toggle the flag
response = authenticated_client.put(f'/api/folders/{mock_folder.id}/toggle')
# Should return 200 OK
assert response.status_code == 200
# Verify the folder was updated in the database
updated_folder = Folder.query.filter_by(id=mock_folder.id).first()
assert updated_folder is not None
assert updated_folder.organize_enabled is False
# Toggle again to make sure it works both ways
response2 = authenticated_client.put(f'/api/folders/{mock_folder.id}/toggle')
# Should return 200 OK
assert response2.status_code == 200
# Verify the folder was updated in the database again
updated_folder2 = Folder.query.filter_by(id=mock_folder.id).first()
assert updated_folder2 is not None
assert updated_folder2.organize_enabled is True
def test_toggle_folder_organize_enabled_not_found(authenticated_client, mock_user):
"""Test toggling organize_enabled flag for a non-existent folder."""
# Try to toggle a folder that doesn't exist
response = authenticated_client.put('/api/folders/999/toggle')
# Should return 404 Not Found
assert response.status_code == 404
def test_toggle_folder_organize_enabled_unauthorized(authenticated_client, mock_user, app):
"""Test toggling organize_enabled flag for a folder that doesn't belong to the user."""
# Create a folder that belongs to a different user
other_user = User(email='other@example.com', first_name='Other', last_name='User')
other_user.set_password('password')
with app.app_context():
db.session.add(other_user)
db.session.commit()
# Create a folder for the other user
other_folder = Folder(
user_id=other_user.id,
name='Other User Folder',
rule_text='Test rule text'
)
db.session.add(other_folder)
db.session.commit()
# Try to toggle the flag for the folder that doesn't belong to authenticated user
response = authenticated_client.put(f'/api/folders/{other_folder.id}/toggle')
# Should return 404 Not Found (folder not found due to authorization check)
assert response.status_code == 404

0
tmp/.hold Normal file
View File

View File

@@ -0,0 +1 @@
3 V1754397400 N1 Gdd33f323d8fa916813000000739f6a6d

View File

@@ -0,0 +1 @@
6891fad8

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,3 @@
V 2
INBOX