wip
This commit is contained in:
parent
f5c8e9ee23
commit
3a16f266da
15 changed files with 511 additions and 169 deletions
|
@ -56,49 +56,53 @@
|
|||
<div class="flex min-h-screen {% if request.cookies.get('sidebar_collapsed') == 'true' %}sidebar-hidden{% endif %}">
|
||||
<!-- Sidebar -->
|
||||
<aside class="w-64 bg-gray-800 h-screen flex-shrink-0 fixed left-0 top-0 z-10 overflow-y-auto transition-all ease-in-out duration-300">
|
||||
<div class="p-4 border-b border-gray-700 flex items-center">
|
||||
<div class="p-4 border-b border-gray-700/30 flex items-center">
|
||||
<h1 class="text-xl font-semibold text-white flex items-center">
|
||||
<i class="mdi mdi-vim text-primary text-2xl mr-2"></i> Vim Docs
|
||||
</h1>
|
||||
</div>
|
||||
<nav class="py-4">
|
||||
<nav class="py-2">
|
||||
<ul class="list-none">
|
||||
<li class="my-1">
|
||||
<a href="{{ url_for('main.index') }}" class="flex items-center px-4 py-2 text-gray-300 hover:text-primary hover:bg-gray-700 rounded-md transition-all {{ 'bg-primary/10 text-primary' if request.endpoint == 'main.index' }}">
|
||||
<li>
|
||||
<a href="{{ url_for('main.index') }}" class="flex items-center px-4 py-2 text-gray-300 hover:text-primary hover:bg-gray-700/50 rounded-md transition-all {{ 'bg-primary/10 text-primary' if request.endpoint == 'main.index' }}">
|
||||
<i class="mdi mdi-home mr-3"></i> Home
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="my-4">
|
||||
<div class="block font-medium text-gray-400 px-4 py-2 flex items-center">
|
||||
<i class="mdi mdi-folder-multiple-outline mr-3"></i> Categories
|
||||
</div>
|
||||
<ul class="ml-3 pl-3 border-l border-gray-700 my-1" id="category-tree">
|
||||
<li class="mt-3">
|
||||
<a href="#" id="root-category-link" class="flex items-center justify-between px-4 py-2 text-gray-300 hover:text-primary hover:bg-gray-700/50 rounded-md transition-all">
|
||||
<div class="flex items-center">
|
||||
<i class="mdi mdi-folder-outline mr-3"></i>
|
||||
<span>Files & Categories</span>
|
||||
</div>
|
||||
<i class="mdi mdi-chevron-down text-sm"></i>
|
||||
</a>
|
||||
<ul class="ml-3 pt-1" id="category-tree">
|
||||
<!-- Categories will be loaded here via JS -->
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="my-4">
|
||||
<div class="block font-medium text-gray-400 px-4 py-2 flex items-center">
|
||||
<li class="mt-3">
|
||||
<div class="block font-medium text-gray-400 px-4 py-1 flex items-center">
|
||||
<i class="mdi mdi-file-document-multiple-outline mr-3"></i> Documents
|
||||
</div>
|
||||
<ul class="ml-3 pl-3 border-l border-gray-700 my-1">
|
||||
<ul class="ml-3 pt-1">
|
||||
<li>
|
||||
<a href="{{ url_for('main.new_document') }}" class="flex items-center py-1 px-2 text-gray-400 hover:text-primary rounded transition-colors {{ 'text-primary' if request.endpoint == 'main.new_document' }}">
|
||||
<a href="{{ url_for('main.new_document') }}" class="flex items-center py-1 px-2 text-gray-400 hover:text-primary hover:bg-gray-700/30 rounded transition-colors {{ 'text-primary' if request.endpoint == 'main.new_document' }}">
|
||||
<i class="mdi mdi-plus-circle-outline mr-2 text-sm"></i> New Document
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ url_for('main.index') }}?view=recent" class="flex items-center py-1 px-2 text-gray-400 hover:text-primary rounded transition-colors">
|
||||
<a href="{{ url_for('main.index') }}?view=recent" class="flex items-center py-1 px-2 text-gray-400 hover:text-primary hover:bg-gray-700/30 rounded transition-colors">
|
||||
<i class="mdi mdi-clock-outline mr-2 text-sm"></i> Recent Documents
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="my-1">
|
||||
<a href="{{ url_for('main.new_document') }}" class="flex items-center px-4 py-2 bg-primary text-black hover:bg-primary-dark rounded-md transition-all">
|
||||
<i class="mdi mdi-plus-circle mr-3"></i> New Document
|
||||
<li class="mt-4 mb-2 px-3">
|
||||
<a href="{{ url_for('main.new_document') }}" class="flex items-center justify-center px-4 py-2 bg-primary text-black hover:bg-primary-dark rounded-md transition-all">
|
||||
<i class="mdi mdi-plus-circle mr-2"></i> New Document
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -188,6 +192,55 @@
|
|||
});
|
||||
}
|
||||
|
||||
// Root category toggle
|
||||
const rootCategoryLink = document.getElementById('root-category-link');
|
||||
const categoryTree = document.getElementById('category-tree');
|
||||
|
||||
if (rootCategoryLink && categoryTree) {
|
||||
// Initialize - expand by default
|
||||
rootCategoryLink.querySelector('i.mdi-chevron-down').classList.add('rotate-180');
|
||||
|
||||
rootCategoryLink.addEventListener('click', function(e) {
|
||||
// Only handle the toggle if clicking on the chevron icon
|
||||
if (e.target.classList.contains('mdi-chevron-down') || e.target.parentElement.classList.contains('mdi-chevron-down')) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// Toggle category tree visibility
|
||||
if (categoryTree.classList.contains('hidden')) {
|
||||
categoryTree.classList.remove('hidden');
|
||||
rootCategoryLink.querySelector('i.mdi-chevron-down').classList.add('rotate-180');
|
||||
} else {
|
||||
categoryTree.classList.add('hidden');
|
||||
rootCategoryLink.querySelector('i.mdi-chevron-down').classList.remove('rotate-180');
|
||||
}
|
||||
|
||||
// Save preference in localStorage
|
||||
localStorage.setItem('rootCategoryExpanded', !categoryTree.classList.contains('hidden'));
|
||||
} else {
|
||||
// If not clicking on the chevron, we need to load the root category
|
||||
fetch('/api/categories')
|
||||
.then(response => response.json())
|
||||
.then(categories => {
|
||||
const rootCategory = categories.find(c => c.is_root);
|
||||
if (rootCategory) {
|
||||
window.location.href = `/category/${rootCategory.id}`;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching root category:', error);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Load saved preference
|
||||
const rootExpanded = localStorage.getItem('rootCategoryExpanded') !== 'false';
|
||||
if (!rootExpanded) {
|
||||
categoryTree.classList.add('hidden');
|
||||
rootCategoryLink.querySelector('i.mdi-chevron-down').classList.remove('rotate-180');
|
||||
}
|
||||
}
|
||||
|
||||
// Load categories
|
||||
loadCategories();
|
||||
|
||||
|
@ -205,20 +258,155 @@
|
|||
categoryTree.innerHTML = ''; // Clear existing items
|
||||
|
||||
if (categories.length === 0) {
|
||||
categoryTree.innerHTML = '<li class="px-4 py-2 text-gray-500">No categories found</li>';
|
||||
categoryTree.innerHTML = '<li class="px-4 py-2 text-gray-500">No items found</li>';
|
||||
return;
|
||||
}
|
||||
|
||||
categories.forEach(category => {
|
||||
categoryTree.appendChild(createCategoryItem(category));
|
||||
});
|
||||
// First add documents without categories (directly in root)
|
||||
const rootCategory = categories.find(c => c.is_root);
|
||||
if (rootCategory && rootCategory.documents && rootCategory.documents.length > 0) {
|
||||
// Create a section header for root documents
|
||||
const docHeader = document.createElement('div');
|
||||
docHeader.className = 'text-xs uppercase text-gray-500 font-medium px-2 py-1 mt-2';
|
||||
docHeader.textContent = 'Files';
|
||||
categoryTree.appendChild(docHeader);
|
||||
|
||||
// Create a container for documents
|
||||
const docsContainer = document.createElement('div');
|
||||
docsContainer.className = 'mb-2';
|
||||
categoryTree.appendChild(docsContainer);
|
||||
|
||||
const documentsUl = document.createElement('ul');
|
||||
documentsUl.className = 'py-1 space-y-0.5';
|
||||
docsContainer.appendChild(documentsUl);
|
||||
|
||||
rootCategory.documents.forEach(docId => {
|
||||
// Fetch document details and add to the tree
|
||||
fetch(`/api/document/${docId}`)
|
||||
.then(response => response.json())
|
||||
.then(doc => {
|
||||
const docLi = document.createElement('li');
|
||||
docLi.className = 'document-item relative group';
|
||||
|
||||
const docLink = document.createElement('a');
|
||||
docLink.href = `/document/${doc.id}`;
|
||||
docLink.className = 'flex items-center py-1 px-2 text-gray-400 hover:text-primary hover:bg-gray-700/30 rounded transition-colors truncate';
|
||||
docLink.innerHTML = `<i class="mdi mdi-file-document-outline mr-2 text-sm"></i> <span class="truncate">${doc.title}</span>`;
|
||||
|
||||
// Add drag functionality
|
||||
docLink.draggable = true;
|
||||
docLink.dataset.docId = doc.id;
|
||||
docLink.addEventListener('dragstart', function(e) {
|
||||
e.dataTransfer.setData('text/plain', JSON.stringify({
|
||||
type: 'document',
|
||||
id: doc.id,
|
||||
title: doc.title
|
||||
}));
|
||||
docLi.classList.add('dragging');
|
||||
});
|
||||
|
||||
docLink.addEventListener('dragend', function() {
|
||||
docLi.classList.remove('dragging');
|
||||
});
|
||||
|
||||
// Add document actions
|
||||
const docActions = createDocumentActions(doc, docLi, rootCategory.id);
|
||||
|
||||
docLi.appendChild(docLink);
|
||||
docLi.appendChild(docActions);
|
||||
documentsUl.appendChild(docLi);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(`Error fetching document ${docId}:`, error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// If there are any non-root categories, add a section header
|
||||
const nonRootCategories = categories.filter(c => !c.is_root);
|
||||
if (nonRootCategories.length > 0) {
|
||||
const catHeader = document.createElement('div');
|
||||
catHeader.className = 'text-xs uppercase text-gray-500 font-medium px-2 py-1 mt-3';
|
||||
catHeader.textContent = 'Categories';
|
||||
categoryTree.appendChild(catHeader);
|
||||
|
||||
// Then add all non-root categories
|
||||
nonRootCategories.forEach(category => {
|
||||
categoryTree.appendChild(createCategoryItem(category));
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error loading categories:', error);
|
||||
categoryTree.innerHTML = '<li class="px-4 py-2 text-red-500">Error loading categories</li>';
|
||||
categoryTree.innerHTML = '<li class="px-4 py-2 text-red-500">Error loading items</li>';
|
||||
});
|
||||
}
|
||||
|
||||
function createDocumentActions(doc, docLi, categoryId) {
|
||||
const docActions = document.createElement('div');
|
||||
docActions.className = 'actions absolute right-0 hidden group-hover:flex items-center bg-gray-800/90 px-1 rounded-sm';
|
||||
|
||||
// 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 = '<i class="mdi mdi-pencil-outline text-sm"></i>';
|
||||
editBtn.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
window.location.href = `/document/${doc.id}/edit`;
|
||||
});
|
||||
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 = '<i class="mdi mdi-delete-outline text-sm"></i>';
|
||||
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 = categoryId ? `/category/${categoryId}` : '/';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showNotification('Error deleting document', 'error');
|
||||
});
|
||||
}
|
||||
});
|
||||
docActions.appendChild(deleteBtn);
|
||||
|
||||
return docActions;
|
||||
}
|
||||
|
||||
function createCategoryItem(category) {
|
||||
const li = document.createElement('li');
|
||||
li.className = 'category-item my-1';
|
||||
|
@ -248,7 +436,7 @@
|
|||
// Create the link to view the category
|
||||
const a = document.createElement('a');
|
||||
a.href = `/category/${category.id}`;
|
||||
let categoryClass = 'flex-grow flex items-center py-1 px-2 text-gray-400 hover:text-primary rounded transition-colors overflow-hidden';
|
||||
let categoryClass = 'flex-grow flex items-center py-1 px-2 text-gray-400 hover:text-primary hover:bg-gray-700/30 rounded transition-colors overflow-hidden';
|
||||
|
||||
// Special styling for root
|
||||
if (category.is_root) {
|
||||
|
@ -264,7 +452,7 @@
|
|||
|
||||
// Create actions container that appears on hover
|
||||
const actionsContainer = document.createElement('div');
|
||||
actionsContainer.className = 'actions absolute right-0 hidden group-hover:flex items-center bg-gray-800 px-1';
|
||||
actionsContainer.className = 'actions absolute right-0 hidden group-hover:flex items-center bg-gray-800/90 px-1 rounded-sm';
|
||||
categoryContainer.appendChild(actionsContainer);
|
||||
|
||||
// Add document button - consistent across all views
|
||||
|
@ -305,7 +493,7 @@
|
|||
|
||||
// Create the child container for documents and subcategories
|
||||
const childrenContainer = document.createElement('div');
|
||||
childrenContainer.className = 'ml-2 pl-2 border-l border-gray-700 mt-1 mb-1 overflow-hidden transition-all duration-300';
|
||||
childrenContainer.className = 'ml-4 mt-1 mb-1 overflow-hidden transition-all duration-300';
|
||||
childrenContainer.style.display = 'none'; // Initially collapsed
|
||||
li.appendChild(childrenContainer);
|
||||
|
||||
|
@ -357,7 +545,7 @@
|
|||
// Add documents first
|
||||
if (hasDocuments) {
|
||||
const documentsUl = document.createElement('ul');
|
||||
documentsUl.className = 'py-1 space-y-1';
|
||||
documentsUl.className = 'py-1 space-y-0.5';
|
||||
childrenContainer.appendChild(documentsUl);
|
||||
|
||||
// Sort documents by name
|
||||
|
@ -374,7 +562,7 @@
|
|||
|
||||
const docLink = document.createElement('a');
|
||||
docLink.href = `/document/${doc.id}`;
|
||||
docLink.className = 'flex items-center py-1 px-2 text-gray-400 hover:text-primary rounded transition-colors truncate';
|
||||
docLink.className = 'flex items-center py-1 px-2 text-gray-400 hover:text-primary hover:bg-gray-700/30 rounded transition-colors truncate';
|
||||
docLink.innerHTML = `<i class="mdi mdi-file-document-outline mr-2 text-sm"></i> <span class="truncate">${doc.title}</span>`;
|
||||
|
||||
// Add drag functionality
|
||||
|
@ -394,66 +582,7 @@
|
|||
});
|
||||
|
||||
// Add actions for documents
|
||||
const docActions = document.createElement('div');
|
||||
docActions.className = 'actions absolute right-0 hidden group-hover:flex items-center bg-gray-800 px-1';
|
||||
|
||||
// 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 = '<i class="mdi mdi-pencil-outline text-sm"></i>';
|
||||
editBtn.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
window.location.href = `/document/${doc.id}/edit`;
|
||||
});
|
||||
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 = '<i class="mdi mdi-delete-outline text-sm"></i>';
|
||||
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);
|
||||
const docActions = createDocumentActions(doc, docLi, category.id);
|
||||
|
||||
docLi.appendChild(docLink);
|
||||
docLi.appendChild(docActions);
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ category.name }} - Vim Docs{% endblock %}
|
||||
{% block title %}{{ category.display_name }} - Vim Docs{% endblock %}
|
||||
|
||||
{% block header_title %}
|
||||
<i class="mdi {{ category.icon }} mr-2"></i> {{ category.name }}
|
||||
<i class="mdi {{ category.icon }} mr-2"></i> {{ category.display_name }}
|
||||
{% endblock %}
|
||||
|
||||
{% block header_actions %}
|
||||
<a href="{{ url_for('main.new_document') }}?category={{ category.id }}" class="inline-flex items-center px-4 py-2 bg-primary text-black rounded-md hover:bg-primary-dark transition-colors">
|
||||
<i class="mdi mdi-plus mr-2"></i> New Document
|
||||
</a>
|
||||
<a href="{{ url_for('main.new_category') }}?parent_id={{ category.id }}" class="inline-flex items-center px-4 py-2 bg-primary/80 text-black rounded-md hover:bg-primary-dark transition-colors ml-2">
|
||||
<i class="mdi mdi-folder-plus-outline mr-2"></i> New Subcategory
|
||||
<a href="{{ url_for('main.new_category') }}{% if not category.is_root %}?parent_id={{ category.id }}{% endif %}" class="inline-flex items-center px-4 py-2 bg-primary/80 text-black rounded-md hover:bg-primary-dark transition-colors ml-2">
|
||||
<i class="mdi mdi-folder-plus-outline mr-2"></i> {% if category.is_root %}New Category{% else %}New Subcategory{% endif %}
|
||||
</a>
|
||||
{% if not category.is_root %}
|
||||
<a href="{{ url_for('main.edit_category', category_id=category.id) }}" class="inline-flex items-center px-4 py-2 bg-gray-700 text-white rounded-md hover:bg-gray-600 transition-colors ml-2">
|
||||
<i class="mdi mdi-pencil mr-2"></i> Edit Category
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="{{ url_for('main.category_all_documents', category_id=category.id) }}" class="inline-flex items-center px-4 py-2 bg-gray-700 text-white rounded-md hover:bg-gray-600 transition-colors ml-2">
|
||||
<i class="mdi mdi-file-document-multiple-outline mr-2"></i> All Documents
|
||||
</a>
|
||||
|
@ -29,14 +31,18 @@
|
|||
<div class="w-10 h-10 rounded-md bg-primary/20 flex items-center justify-center text-primary mr-3">
|
||||
<i class="mdi {{ category.icon }} text-2xl"></i>
|
||||
</div>
|
||||
<h1 class="text-2xl font-bold text-white">{{ category.name }}</h1>
|
||||
<h1 class="text-2xl font-bold text-white">{{ category.display_name }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="text-gray-400 mb-4">
|
||||
{% if category.description %}
|
||||
{{ category.description }}
|
||||
{% else %}
|
||||
{% if category.is_root %}
|
||||
The default container for all your documents and categories
|
||||
{% else %}
|
||||
A category for organizing your documents
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
@ -46,10 +52,12 @@
|
|||
<a href="{{ url_for('main.index') }}" class="hover:text-primary">Home</a>
|
||||
{% if category.parent %}
|
||||
<span class="mx-2">/</span>
|
||||
<a href="{{ url_for('main.view_category', category_id=category.parent.id) }}" class="hover:text-primary">{{ category.parent.name }}</a>
|
||||
<a href="{{ url_for('main.view_category', category_id=category.parent.id) }}" class="hover:text-primary">{{ category.parent.display_name }}</a>
|
||||
{% endif %}
|
||||
{% if not category.is_root %}
|
||||
<span class="mx-2">/</span>
|
||||
<span class="text-gray-400">{{ category.name }}</span>
|
||||
<span class="text-gray-400">{{ category.display_name }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -72,7 +80,7 @@
|
|||
<div class="w-10 h-10 rounded-md bg-primary/20 flex items-center justify-center text-primary mr-3">
|
||||
<i class="mdi {{ subcategory.icon }} text-2xl"></i>
|
||||
</div>
|
||||
<h3 class="text-white font-medium truncate">{{ subcategory.name }}</h3>
|
||||
<h3 class="text-white font-medium truncate">{{ subcategory.display_name }}</h3>
|
||||
</div>
|
||||
|
||||
{% if subcategory.description %}
|
||||
|
@ -94,7 +102,7 @@
|
|||
<i class="mdi mdi-folder-plus-outline text-2xl text-gray-500"></i>
|
||||
</div>
|
||||
<h3 class="text-gray-400 font-medium mb-1">New Subcategory</h3>
|
||||
<p class="text-gray-500 text-sm">Add a subcategory to {{ category.name }}</p>
|
||||
<p class="text-gray-500 text-sm">Add a subcategory to {{ category.display_name }}</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -8,9 +8,15 @@
|
|||
<button id="save-button" class="inline-flex items-center px-4 py-2 bg-primary text-black rounded-md hover:bg-primary-dark transition-colors">
|
||||
<i class="mdi mdi-content-save mr-2"></i> Save
|
||||
</button>
|
||||
{% if category %}
|
||||
<a href="{{ url_for('main.view_category', category_id=category.id) }}" class="inline-flex items-center px-4 py-2 bg-gray-700 text-white rounded-md hover:bg-gray-600 transition-colors ml-2">
|
||||
<i class="mdi mdi-arrow-left mr-2"></i> Back
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('main.index') }}" class="inline-flex items-center px-4 py-2 bg-gray-700 text-white rounded-md hover:bg-gray-600 transition-colors ml-2">
|
||||
<i class="mdi mdi-arrow-left mr-2"></i> Back
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if category and not category.is_root %}
|
||||
<button id="delete-category-btn" class="inline-flex items-center px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-500 transition-colors ml-2">
|
||||
<i class="mdi mdi-delete mr-2"></i> Delete
|
||||
|
@ -91,7 +97,7 @@
|
|||
<label for="category-parent" class="block text-sm font-medium text-gray-400 mb-1">Parent Category</label>
|
||||
<select id="category-parent"
|
||||
class="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-md text-white focus:outline-none focus:ring-2 focus:ring-primary focus:border-primary">
|
||||
<option value="">None (Root Category)</option>
|
||||
<option value="">No Parent</option>
|
||||
<!-- Options will be populated with JavaScript -->
|
||||
</select>
|
||||
</div>
|
||||
|
@ -202,13 +208,13 @@
|
|||
|
||||
<div class="p-6">
|
||||
<p class="text-gray-300 mb-6">
|
||||
Are you sure you want to delete <span class="font-semibold text-white">{{ category.name if category else 'this category' }}</span>?
|
||||
{% if category and (category.documents.count() > 0 or category.children.count() > 0) %}
|
||||
Are you sure you want to delete <span class="font-semibold text-white">{{ category.name if category is not none else 'this category' }}</span>?
|
||||
{% if category is not none and (category.documents.count() > 0 or category.children.count() > 0) %}
|
||||
This category contains {{ category.documents.count() }} document(s) and {{ category.children.count() }} subcategory(ies).
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
{% if category and (category.documents.count() > 0 or category.children.count() > 0) %}
|
||||
{% if category is not none and (category.documents.count() > 0 or category.children.count() > 0) %}
|
||||
<div class="mb-6">
|
||||
<p class="text-white mb-2">What should happen to the contents?</p>
|
||||
<div class="space-y-3">
|
||||
|
@ -266,15 +272,15 @@
|
|||
fetch('/api/categories')
|
||||
.then(response => response.json())
|
||||
.then(categories => {
|
||||
// Find the root category
|
||||
const rootCategory = categories.find(c => c.name === 'root');
|
||||
|
||||
// Add options recursively
|
||||
function addCategoryOptions(categories, depth = 0) {
|
||||
categories.forEach(category => {
|
||||
// Skip the category being edited to avoid circular references
|
||||
{% if category %}
|
||||
if (category.id === {{ category.id }}) return;
|
||||
// Also skip any root categories as they shouldn't be selectable as parents
|
||||
{% if category is not none %}
|
||||
if (category.id === {{ category.id }} || category.is_root) return;
|
||||
{% else %}
|
||||
if (category.is_root) return;
|
||||
{% endif %}
|
||||
|
||||
const option = document.createElement('option');
|
||||
|
@ -287,23 +293,16 @@
|
|||
// Select option logic:
|
||||
// 1. If we have a parent specified, select that parent
|
||||
// 2. If we're editing an existing category, select its current parent
|
||||
// 3. If creating a new category, select root by default
|
||||
let selectThisOption = false;
|
||||
|
||||
{% if parent %}
|
||||
{% if parent is not none %}
|
||||
if (category.id === {{ parent.id }}) {
|
||||
selectThisOption = true;
|
||||
}
|
||||
{% elif category and category.parent_id %}
|
||||
{% elif category is not none and category.parent_id is not none %}
|
||||
if (category.id === {{ category.parent_id }}) {
|
||||
selectThisOption = true;
|
||||
}
|
||||
{% else %}
|
||||
// If no parent specified and creating new category, default to root
|
||||
if (category.is_root && !rootSelected) {
|
||||
selectThisOption = true;
|
||||
rootSelected = true;
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
option.selected = selectThisOption;
|
||||
|
@ -315,10 +314,7 @@
|
|||
});
|
||||
}
|
||||
|
||||
// Initialize flag to track if root has been selected
|
||||
let rootSelected = false;
|
||||
|
||||
// Start from root categories
|
||||
// Start from categories
|
||||
addCategoryOptions(categories);
|
||||
})
|
||||
.catch(error => {
|
||||
|
@ -538,7 +534,8 @@
|
|||
const queryParams = deleteOption === 'delete' ? '' : '?preserve_contents=true';
|
||||
|
||||
// Send delete request
|
||||
fetch(`/api/category/{{ category.id if category else '' }}${queryParams}`, {
|
||||
{% if category is not none %}
|
||||
fetch(`/api/category/{{ category.id }}${queryParams}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
@ -568,7 +565,7 @@
|
|||
|
||||
// Redirect to home page or parent category
|
||||
setTimeout(() => {
|
||||
{% if category and category.parent_id %}
|
||||
{% if category.parent_id %}
|
||||
window.location.href = '/category/{{ category.parent_id }}';
|
||||
{% else %}
|
||||
window.location.href = '/';
|
||||
|
@ -590,6 +587,7 @@
|
|||
setTimeout(() => notification.remove(), 300);
|
||||
}, 3000);
|
||||
});
|
||||
{% endif %}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -535,6 +535,12 @@
|
|||
const listView = document.getElementById('list-view');
|
||||
|
||||
if (gridViewBtn && listViewBtn) {
|
||||
// Initialize view (grid is default)
|
||||
gridViewBtn.classList.add('active-view');
|
||||
listViewBtn.classList.remove('active-view');
|
||||
gridView.classList.remove('hidden');
|
||||
listView.classList.add('hidden');
|
||||
|
||||
// Load saved preference
|
||||
const savedView = localStorage.getItem('categoryViewPreference');
|
||||
if (savedView === 'list') {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue