From f5c8e9ee23ca1f387a7beb88e7407c2a3c07e7b2 Mon Sep 17 00:00:00 2001 From: pika Date: Thu, 17 Apr 2025 12:05:22 +0200 Subject: [PATCH] wip --- app/routes.py | 57 ++-- app/templates/base.html | 194 ++++++++++++- app/templates/category_documents.html | 397 +++++++++++++++++++++++++- app/templates/category_edit.html | 152 +++++++++- 4 files changed, 756 insertions(+), 44 deletions(-) diff --git a/app/routes.py b/app/routes.py index c05f308..76f055f 100644 --- a/app/routes.py +++ b/app/routes.py @@ -296,28 +296,49 @@ def delete_category(category_id): if category.is_root: return jsonify({'error': 'Cannot delete root category'}), 400 - # Get target category for documents if specified - new_category_id = request.args.get('new_category_id') - if new_category_id: - new_category = Category.query.filter_by(id=new_category_id, user_id=current_user.id).first() - if new_category: - # Reassign documents - for doc in category.documents: - doc.category_id = new_category.id - else: - # Move documents to no category + # Check if we should preserve contents + preserve_contents = request.args.get('preserve_contents', 'false').lower() == 'true' + + # Find parent category or root + parent_category = None + if category.parent_id: + parent_category = Category.query.filter_by(id=category.parent_id, user_id=current_user.id).first() + + if not parent_category: + # If no parent or parent not found, use root category + parent_category = Category.query.filter_by(user_id=current_user.id, is_root=True).first() + + if preserve_contents: + # Move documents to parent category for doc in category.documents: - doc.category_id = None - - # Also handle child categories - for child in category.children: - if new_category_id: - child.parent_id = new_category_id - else: - child.parent_id = None + doc.category_id = parent_category.id + + # Move child categories to parent + for child in category.children: + child.parent_id = parent_category.id + else: + # Delete all documents in this category + for doc in category.documents: + db.session.delete(doc) + + # Recursively delete subcategories and their documents + def delete_subcategories(parent): + for child in parent.children: + # Delete all documents in this subcategory + for doc in child.documents: + db.session.delete(doc) + # Recursively delete subcategories + delete_subcategories(child) + # Delete the subcategory itself + db.session.delete(child) + + # Start recursive deletion + delete_subcategories(category) + # Delete the category itself db.session.delete(category) db.session.commit() + return jsonify({'success': True}) @main.route('/api/search', methods=['GET']) diff --git a/app/templates/base.html b/app/templates/base.html index 3de4985..e68e656 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -397,23 +397,71 @@ const docActions = document.createElement('div'); docActions.className = 'actions absolute right-0 hidden group-hover:flex items-center bg-gray-800 px-1'; - // Edit doc button - const editDocButton = document.createElement('button'); - editDocButton.className = 'p-1 text-gray-500 hover:text-primary rounded transition-colors'; - editDocButton.title = 'Edit Document'; - editDocButton.innerHTML = ''; - editDocButton.addEventListener('click', function(e) { + // Edit document button + const editBtn = document.createElement('button'); + editBtn.className = 'p-1 text-gray-500 hover:text-primary rounded transition-colors'; + editBtn.title = 'Edit document'; + editBtn.innerHTML = ''; + editBtn.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); window.location.href = `/document/${doc.id}/edit`; }); - docActions.appendChild(editDocButton); + docActions.appendChild(editBtn); + + // Delete document button + const deleteBtn = document.createElement('button'); + deleteBtn.className = 'p-1 text-gray-500 hover:text-red-500 rounded transition-colors'; + deleteBtn.title = 'Delete document'; + deleteBtn.innerHTML = ''; + deleteBtn.addEventListener('click', function(e) { + e.preventDefault(); + e.stopPropagation(); + + // Show confirmation dialog + if (confirm(`Are you sure you want to delete "${doc.title}"? This cannot be undone.`)) { + // Send delete request + fetch(`/api/document/${doc.id}`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': getCsrfToken() + } + }) + .then(response => { + if (!response.ok) { + throw new Error('Failed to delete'); + } + return response.json(); + }) + .then(data => { + // Remove the document from the DOM + docLi.remove(); + + // Show notification + showNotification('Document deleted successfully'); + + // If we're on the document's page, redirect to the category or home + const currentPath = window.location.pathname; + if (currentPath === `/document/${doc.id}` || currentPath === `/document/${doc.id}/edit`) { + window.location.href = `/category/${category.id}`; + } + }) + .catch(error => { + console.error('Error:', error); + showNotification('Error deleting document', 'error'); + }); + } + }); + docActions.appendChild(deleteBtn); docLi.appendChild(docLink); docLi.appendChild(docActions); documentsUl.appendChild(docLi); }) - .catch(error => console.error('Error fetching document:', error)); + .catch(error => { + console.error(`Error fetching document ${docId}:`, error); + }); }); } @@ -477,10 +525,12 @@ } // Helper function to show notifications - function showNotification(message) { + function showNotification(message, type = 'success') { const notification = document.createElement('div'); - notification.className = 'fixed bottom-4 right-4 bg-primary/90 text-white px-4 py-2 rounded-md shadow-lg transform translate-y-0 opacity-100 transition-all duration-300 flex items-center'; - notification.innerHTML = `${message}`; + const bgColor = type === 'success' ? 'bg-green-600' : 'bg-red-600'; + + notification.className = `fixed bottom-4 right-4 ${bgColor} text-white px-4 py-2 rounded-md shadow-lg transform translate-y-0 opacity-100 transition-all duration-300 z-50`; + notification.textContent = message; document.body.appendChild(notification); @@ -490,7 +540,7 @@ }, 3000); } - // Helper function to get CSRF token from meta tag + // Helper function to get CSRF token function getCsrfToken() { return document.querySelector('meta[name="csrf-token"]').getAttribute('content'); } @@ -589,5 +639,125 @@ {% block extra_js %}{% endblock %} + + + + + + \ No newline at end of file diff --git a/app/templates/category_documents.html b/app/templates/category_documents.html index 135edac..fd42daf 100644 --- a/app/templates/category_documents.html +++ b/app/templates/category_documents.html @@ -51,6 +51,104 @@ -webkit-line-clamp: 3; -webkit-box-orient: vertical; text-overflow: ellipsis; + transition: max-height 0.3s ease; + } + + .document-preview.expanded { + max-height: 20rem; + -webkit-line-clamp: unset; + overflow-y: auto; + } + + .document-preview.fully-expanded { + max-height: 60rem; + -webkit-line-clamp: unset; + overflow-y: auto; + } + + .preview-fold-btn { + display: block; + width: 100%; + text-align: center; + padding: 0.25rem; + color: #58a6ff; + background-color: rgba(88, 166, 255, 0.05); + border-radius: 0.25rem; + cursor: pointer; + font-size: 0.75rem; + margin-top: 0.5rem; + transition: background-color 0.2s ease; + } + + .preview-fold-btn:hover { + background-color: rgba(88, 166, 255, 0.1); + } + + .document-preview .markdown-content { + font-size: 0.875rem; + } + + .document-preview .markdown-content h1 { + font-size: 1.2rem; + margin-top: 0.5rem; + margin-bottom: 0.5rem; + } + + .document-preview .markdown-content h2 { + font-size: 1.1rem; + margin-top: 0.5rem; + margin-bottom: 0.5rem; + } + + .document-preview .markdown-content h3, + .document-preview .markdown-content h4, + .document-preview .markdown-content h5, + .document-preview .markdown-content h6 { + font-size: 1rem; + margin-top: 0.5rem; + margin-bottom: 0.5rem; + } + + .document-preview .markdown-content p { + margin-bottom: 0.5rem; + } + + .document-preview .markdown-content ul, + .document-preview .markdown-content ol { + padding-left: 1.5rem; + margin-bottom: 0.5rem; + } + + .document-preview .markdown-content pre { + background-color: #1e1e2e; + padding: 0.5rem; + border-radius: 0.25rem; + overflow-x: auto; + margin-bottom: 0.5rem; + } + + .document-preview .markdown-content blockquote { + border-left: 3px solid #30363d; + padding-left: 0.5rem; + color: #8b949e; + margin-bottom: 0.5rem; + } + + .document-preview .markdown-content img { + max-width: 100%; + height: auto; + } + + .document-preview .markdown-content table { + width: 100%; + border-collapse: collapse; + margin-bottom: 0.5rem; + } + + .document-preview .markdown-content th, + .document-preview .markdown-content td { + border: 1px solid #30363d; + padding: 0.25rem 0.5rem; } .document-tag { @@ -91,6 +189,49 @@ background-color: rgba(80, 250, 123, 0.2); color: #50fa7b; } + + .document-actions { + display: flex; + gap: 0.25rem; + } + + .action-btn { + padding: 0.25rem; + border-radius: 0.25rem; + color: #8b949e; + transition: all 0.2s ease; + } + + .action-btn:hover { + background-color: rgba(255, 255, 255, 0.1); + } + + .action-btn.delete:hover { + color: #f85149; + background-color: rgba(248, 81, 73, 0.1); + } + + .confirm-delete-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 50; + } + + .confirm-delete-content { + background-color: #2d333b; + border-radius: 0.5rem; + width: 90%; + max-width: 28rem; + padding: 1.5rem; + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5); + } {% endblock %} @@ -108,7 +249,7 @@
-
@@ -158,9 +302,13 @@ {% endif %}
- {{ doc.content[:200] }}{% if doc.content|length > 200 %}...{% endif %} +
+ +
+ +
@@ -220,20 +368,29 @@
- {{ doc.content[:200] }}{% if doc.content|length > 200 %}...{% endif %} +
+ +
+ +
- - - - - - - - - +
+ + + + + + + + + + +
@@ -259,11 +416,130 @@

Try a different search term

+ + + {% endblock %} {% block extra_js %} + + {% endblock %} \ No newline at end of file