finished category and tag error correction.
This commit is contained in:
parent
893ccd558f
commit
40838bd134
@ -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;
|
||||
}
|
||||
@ -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}
|
||||
|
||||
@ -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'
|
||||
|
||||
|
||||
0
client/kb-frontend/src/styles/shared.css
Normal file
0
client/kb-frontend/src/styles/shared.css
Normal file
0
client/kb-frontend/src/styles/variable.css
Normal file
0
client/kb-frontend/src/styles/variable.css
Normal file
Loading…
x
Reference in New Issue
Block a user