finished category and tag error correction.

This commit is contained in:
MattLeo 2025-12-08 14:24:15 -06:00
parent 893ccd558f
commit 40838bd134
5 changed files with 153 additions and 15 deletions

View File

@ -263,3 +263,64 @@
.badge-remove:hover { .badge-remove:hover {
opacity: 1; opacity: 1;
} }
.create-new-section {
padding: 0.75rem;
border-bottom: 1px solid #ddd;
background-color: #f9f9f9;
}
.create-new-section input {
width: 100%;
padding: 0.5rem;
border: 1px solid #ddd;
border-radius: 4px;
margin-bottom: 0.5rem;
}
.create-new-buttons {
display: flex;
gap: 0.5rem;
}
.create-new-buttons button {
padding: 0.4rem 0.8rem;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 0.85rem;
}
.create-new-buttons .create-btn {
background-color: #27ae60;
color: white;
}
.create-new-buttons .create-btn:hover {
background-color: #229954;
}
.create-new-buttons .cancel-btn {
background-color: #95a5a6;
color: white;
}
.create-new-buttons .cancel-btn:hover {
background-color: #7f8c8d;
}
.create-new-button-dropdown {
width: 100%;
padding: 0.75rem;
background-color: #27ae60;
color: white;
border: none;
border-bottom: 1px solid #ddd;
text-align: left;
cursor: pointer;
font-weight: bold;
}
.create-new-button-dropdown:hover {
background-color: #229954;
}

View File

@ -65,7 +65,8 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
const [selectedTags, setSelectedTags] = useState([]); const [selectedTags, setSelectedTags] = useState([]);
const [tagInput, setTagInput] = useState(''); const [tagInput, setTagInput] = useState('');
const [showCategoryDropdown, setShowCategoryDropdown] = useState(false); const [showCategoryDropdown, setShowCategoryDropdown] = useState(false);
//const [showTagDropdown, setShowTagDropdown] = useState(false); const [showCreateCategory, setShowCreateCategory] = useState(false);
const [newCategoryInput, setNewCategoryInput] = useState('');
const [showTagSuggestions, setShowTagSuggestions] = useState(false); const [showTagSuggestions, setShowTagSuggestions] = useState(false);
const quillRef = useRef(null); const quillRef = useRef(null);
@ -95,7 +96,7 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
const data = await response.json(); const data = await response.json();
setAvailableCategories(data.map(cat => cat.name)); setAvailableCategories(data.map(cat => cat.name));
} }
} catch(err) { } catch (err) {
console.error('Error fetching categories:', err); console.error('Error fetching categories:', err);
} }
}; };
@ -112,7 +113,7 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
const data = await response.json(); const data = await response.json();
setAvailableTags(data.map(tag => tag.name)); setAvailableTags(data.map(tag => tag.name));
} }
} catch(err) { } catch (err) {
console.error('Error fetching tags:', err); console.error('Error fetching tags:', err);
} }
}; };
@ -149,7 +150,7 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
body: JSON.stringify({ tags: selectedTags }) body: JSON.stringify({ tags: selectedTags })
}); });
} catch(err) { } catch (err) {
console.error('Error saving categories/tags:', err); console.error('Error saving categories/tags:', err);
} }
}; };
@ -187,6 +188,35 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
setIsGalleryOpen(!isGalleryOpen); setIsGalleryOpen(!isGalleryOpen);
}; };
const handleCreateNewCategory = async () => {
if (!newCategoryInput.trim()) return;
try {
const response = await fetch('http://localhost:9000/api/categories', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: newCategoryInput.trim() })
});
if (response.ok) {
const data = await response.json();
setAvailableCategories([...availableCategories, data.name]);
setSelectedCategories([...selectedCategories, data.name]);
setNewCategoryInput('');
setShowCreateCategory(false);
} else {
const data = await response.json();
alert(data.error || 'Failed to create cateogry');
}
} catch (err) {
console.error('Failed to create new category:', err);
alert('Failed to create new category');
}
};
const modules = { const modules = {
toolbar: [ toolbar: [
[{ 'header': [1, 2, 3, false] }], [{ 'header': [1, 2, 3, false] }],
@ -295,6 +325,51 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
{showCategoryDropdown && ( {showCategoryDropdown && (
<div className='category-dropdown'> <div className='category-dropdown'>
{showCreateCategory ? (
<div className='create-new-section'>
<input
type='text'
placeholder='New category name...'
value={newCategoryInput}
onChange={(e) => setNewCategoryInput(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
handleCreateNewCategory();
}
}}
autoFocus
/>
<div className='create-new-buttons'>
<button
type='button'
className='create-btn'
onClick={handleCreateNewCategory}
>
Create
</button>
<button
type='button'
className='cancel-btn'
onClick={() => {
setShowCreateCategory(false);
setNewCategoryInput('');
}}
>
Cancel
</button>
</div>
</div>
) : (
<button
type='button'
className='create-new-button-dropdown'
onClick={() => setShowCreateCategory(true)}
>
+ Create New Category
</button>
)}
{availableCategories.length === 0 ? ( {availableCategories.length === 0 ? (
<div className='no-options'>No categories available</div> <div className='no-options'>No categories available</div>
) : ( ) : (

View File

@ -1,5 +1,7 @@
import { StrictMode } from 'react' import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client' import { createRoot } from 'react-dom/client'
import './styles/variables.css';
import './styles/shared.css';
import './index.css' import './index.css'
import App from './App.jsx' import App from './App.jsx'

View File