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

@ -262,4 +262,65 @@
.badge-remove:hover {
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 [tagInput, setTagInput] = useState('');
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 quillRef = useRef(null);
@ -95,7 +96,7 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
const data = await response.json();
setAvailableCategories(data.map(cat => cat.name));
}
} catch(err) {
} catch (err) {
console.error('Error fetching categories:', err);
}
};
@ -112,7 +113,7 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
const data = await response.json();
setAvailableTags(data.map(tag => tag.name));
}
} catch(err) {
} catch (err) {
console.error('Error fetching tags:', err);
}
};
@ -137,7 +138,7 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ categories: selectedCategories })
body: JSON.stringify({ categories: selectedCategories })
});
await fetch(`http://localhost:9000/api/articles/${article.ka_number}/tags`, {
@ -149,7 +150,7 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
body: JSON.stringify({ tags: selectedTags })
});
} catch(err) {
} catch (err) {
console.error('Error saving categories/tags:', err);
}
};
@ -187,6 +188,35 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
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 = {
toolbar: [
[{ 'header': [1, 2, 3, false] }],
@ -252,9 +282,9 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
}
};
const filteredTagSuggestions = availableTags.filter(tag =>
tag.toLowerCase().includes(tagInput.toLowerCase())
&& !selectedTags.includes(tag)
const filteredTagSuggestions = availableTags.filter(tag =>
tag.toLowerCase().includes(tagInput.toLowerCase())
&& !selectedTags.includes(tag)
);
return (
@ -282,19 +312,64 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
<div className='form-field'>
<label>Categories:</label>
<div className='category-selector'>
<button
<button
type='button'
className='category-dropdown-btn'
onClick={() => setShowCategoryDropdown(!showCategoryDropdown)}
>
{selectedCategories.length > 0
? `${selectedCategories.length} selected`
{selectedCategories.length > 0
? `${selectedCategories.length} selected`
: 'Select categories'}
<span className='dropdown-arrow'>{showCategoryDropdown ? '▲' : '▼'}</span>
</button>
{showCategoryDropdown && (
<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 ? (
<div className='no-options'>No categories available</div>
) : (
@ -316,7 +391,7 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
{selectedCategories.map(category => (
<span key={category} className='badge category-badge'>
{category}
<button
<button
type='button'
onClick={() => toggleCategory(category)}
className='badge-remove'
@ -363,7 +438,7 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
{selectedTags.map(tag => (
<span key={tag} className='badge tag-badge'>
#{tag}
<button
<button
type='button'
onClick={() => removeTag(tag)}
className='badge-remove'
@ -384,7 +459,7 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
formats={formats}
/>
</div>
<div className='editor-buttons'>
<button className='save-draft-btn' onClick={handleSaveDraft}>
Save Draft
@ -398,7 +473,7 @@ function ArticleEditor({ article, onSave, onCancel, token }) {
</div>
</div>
<MediaGallery
<MediaGallery
kaNumber={article.ka_number}
token={token}
onInsertMedia={handleInsertMedia}

View File

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

View File