refactor: remove category feature from UI, focus on tags
All checks were successful
Basil CI/CD Pipeline / Shared Package Tests (push) Successful in 1m22s
Basil CI/CD Pipeline / Code Linting (push) Successful in 1m30s
Basil CI/CD Pipeline / Web Tests (push) Successful in 1m37s
Basil CI/CD Pipeline / API Tests (push) Successful in 1m50s
Basil CI/CD Pipeline / Security Scanning (push) Successful in 1m16s
Basil CI/CD Pipeline / Build All Packages (push) Successful in 1m32s
Basil CI/CD Pipeline / E2E Tests (push) Has been skipped
Basil CI/CD Pipeline / Build & Push Docker Images (push) Successful in 5m6s
Basil CI/CD Pipeline / Trigger Deployment (push) Successful in 12s
All checks were successful
Basil CI/CD Pipeline / Shared Package Tests (push) Successful in 1m22s
Basil CI/CD Pipeline / Code Linting (push) Successful in 1m30s
Basil CI/CD Pipeline / Web Tests (push) Successful in 1m37s
Basil CI/CD Pipeline / API Tests (push) Successful in 1m50s
Basil CI/CD Pipeline / Security Scanning (push) Successful in 1m16s
Basil CI/CD Pipeline / Build All Packages (push) Successful in 1m32s
Basil CI/CD Pipeline / E2E Tests (push) Has been skipped
Basil CI/CD Pipeline / Build & Push Docker Images (push) Successful in 5m6s
Basil CI/CD Pipeline / Trigger Deployment (push) Successful in 12s
Removed all category-related UI elements from the web application to simplify recipe organization. Users will now use tags exclusively for categorizing recipes and cookbooks. Changes: - Remove category input fields from RecipeForm and UnifiedEditRecipe - Remove category filters from CookbookDetail - Remove category auto-add feature from Cookbooks and EditCookbook - Preserve category data in database for backward compatibility - Keep API category support for future use or migrations This change reduces user confusion by having a single organizational method (tags) instead of overlapping categories and tags. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,6 @@ function CookbookDetail() {
|
||||
// Filters
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [selectedTags, setSelectedTags] = useState<string[]>([]);
|
||||
const [selectedCategory, setSelectedCategory] = useState<string>('');
|
||||
const [selectedCuisine, setSelectedCuisine] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
@@ -71,7 +70,7 @@ function CookbookDetail() {
|
||||
);
|
||||
};
|
||||
|
||||
// Get all unique tags and categories from recipes
|
||||
// Get all unique tags from recipes
|
||||
const getAllTags = (): string[] => {
|
||||
if (!cookbook) return [];
|
||||
const tagSet = new Set<string>();
|
||||
@@ -81,17 +80,6 @@ function CookbookDetail() {
|
||||
return Array.from(tagSet).sort();
|
||||
};
|
||||
|
||||
const getAllCategories = (): string[] => {
|
||||
if (!cookbook) return [];
|
||||
const categorySet = new Set<string>();
|
||||
cookbook.recipes.forEach(recipe => {
|
||||
if (recipe.categories) {
|
||||
recipe.categories.forEach(cat => categorySet.add(cat));
|
||||
}
|
||||
});
|
||||
return Array.from(categorySet).sort();
|
||||
};
|
||||
|
||||
const getAllCuisines = (): string[] => {
|
||||
if (!cookbook) return [];
|
||||
const cuisineSet = new Set<string>();
|
||||
@@ -121,14 +109,6 @@ function CookbookDetail() {
|
||||
if (!hasAllTags) return false;
|
||||
}
|
||||
|
||||
// Category filter
|
||||
if (selectedCategory) {
|
||||
const recipeCategories = recipe.categories || [];
|
||||
if (!recipeCategories.includes(selectedCategory)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Cuisine filter
|
||||
if (selectedCuisine && recipe.cuisine !== selectedCuisine) {
|
||||
return false;
|
||||
@@ -141,7 +121,6 @@ function CookbookDetail() {
|
||||
const clearFilters = () => {
|
||||
setSearchQuery('');
|
||||
setSelectedTags([]);
|
||||
setSelectedCategory('');
|
||||
setSelectedCuisine('');
|
||||
};
|
||||
|
||||
@@ -164,9 +143,8 @@ function CookbookDetail() {
|
||||
|
||||
const filteredRecipes = getFilteredRecipes();
|
||||
const allTags = getAllTags();
|
||||
const allCategories = getAllCategories();
|
||||
const allCuisines = getAllCuisines();
|
||||
const hasActiveFilters = searchQuery || selectedTags.length > 0 || selectedCategory || selectedCuisine;
|
||||
const hasActiveFilters = searchQuery || selectedTags.length > 0 || selectedCuisine;
|
||||
|
||||
return (
|
||||
<div className="cookbook-detail-page">
|
||||
@@ -220,22 +198,6 @@ function CookbookDetail() {
|
||||
</div>
|
||||
|
||||
<div className="filter-row">
|
||||
{allCategories.length > 0 && (
|
||||
<div className="filter-group">
|
||||
<label htmlFor="category-filter">Category:</label>
|
||||
<select
|
||||
id="category-filter"
|
||||
value={selectedCategory}
|
||||
onChange={(e) => setSelectedCategory(e.target.value)}
|
||||
>
|
||||
<option value="">All Categories</option>
|
||||
{allCategories.map(category => (
|
||||
<option key={category} value={category}>{category}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{allCuisines.length > 0 && (
|
||||
<div className="filter-group">
|
||||
<label htmlFor="cuisine-filter">Cuisine:</label>
|
||||
|
||||
@@ -13,16 +13,13 @@ function Cookbooks() {
|
||||
const [showCreateModal, setShowCreateModal] = useState(false);
|
||||
const [newCookbookName, setNewCookbookName] = useState('');
|
||||
const [newCookbookDescription, setNewCookbookDescription] = useState('');
|
||||
const [autoFilterCategories, setAutoFilterCategories] = useState<string[]>([]);
|
||||
const [autoFilterTags, setAutoFilterTags] = useState<string[]>([]);
|
||||
const [autoFilterCookbookTags, setAutoFilterCookbookTags] = useState<string[]>([]);
|
||||
const [cookbookTags, setCookbookTags] = useState<string[]>([]);
|
||||
const [categoryInput, setCategoryInput] = useState('');
|
||||
const [tagInput, setTagInput] = useState('');
|
||||
const [cookbookTagInput, setCookbookTagInput] = useState('');
|
||||
const [cookbookTagFilterInput, setCookbookTagFilterInput] = useState('');
|
||||
const [availableTags, setAvailableTags] = useState<Tag[]>([]);
|
||||
const [availableCategories, setAvailableCategories] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
@@ -41,15 +38,6 @@ function Cookbooks() {
|
||||
setRecentRecipes(recipesResponse.data || []);
|
||||
setAvailableTags(tagsResponse.data || []);
|
||||
|
||||
// Extract unique categories from recent recipes
|
||||
const categories = new Set<string>();
|
||||
(recipesResponse.data || []).forEach(recipe => {
|
||||
if (recipe.categories) {
|
||||
recipe.categories.forEach(cat => categories.add(cat));
|
||||
}
|
||||
});
|
||||
setAvailableCategories(Array.from(categories).sort());
|
||||
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError('Failed to load data');
|
||||
@@ -71,7 +59,6 @@ function Cookbooks() {
|
||||
await cookbooksApi.create({
|
||||
name: newCookbookName,
|
||||
description: newCookbookDescription || undefined,
|
||||
autoFilterCategories: autoFilterCategories.length > 0 ? autoFilterCategories : undefined,
|
||||
autoFilterTags: autoFilterTags.length > 0 ? autoFilterTags : undefined,
|
||||
autoFilterCookbookTags: autoFilterCookbookTags.length > 0 ? autoFilterCookbookTags : undefined,
|
||||
tags: cookbookTags.length > 0 ? cookbookTags : undefined
|
||||
@@ -79,11 +66,9 @@ function Cookbooks() {
|
||||
|
||||
setNewCookbookName('');
|
||||
setNewCookbookDescription('');
|
||||
setAutoFilterCategories([]);
|
||||
setAutoFilterTags([]);
|
||||
setAutoFilterCookbookTags([]);
|
||||
setCookbookTags([]);
|
||||
setCategoryInput('');
|
||||
setTagInput('');
|
||||
setCookbookTagInput('');
|
||||
setCookbookTagFilterInput('');
|
||||
@@ -95,18 +80,6 @@ function Cookbooks() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddCategory = () => {
|
||||
const trimmed = categoryInput.trim();
|
||||
if (trimmed && !autoFilterCategories.includes(trimmed)) {
|
||||
setAutoFilterCategories([...autoFilterCategories, trimmed]);
|
||||
setCategoryInput('');
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveCategory = (category: string) => {
|
||||
setAutoFilterCategories(autoFilterCategories.filter(c => c !== category));
|
||||
};
|
||||
|
||||
const handleAddTag = () => {
|
||||
const trimmed = tagInput.trim();
|
||||
if (trimmed && !autoFilterTags.includes(trimmed)) {
|
||||
@@ -293,35 +266,6 @@ function Cookbooks() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label>Auto-Add Categories (Optional)</label>
|
||||
<p className="help-text">Recipes with these categories will be automatically added to this cookbook</p>
|
||||
<div className="filter-chips">
|
||||
{autoFilterCategories.map(category => (
|
||||
<span key={category} className="filter-chip">
|
||||
{category}
|
||||
<button type="button" onClick={() => handleRemoveCategory(category)}>×</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="input-with-button">
|
||||
<input
|
||||
type="text"
|
||||
value={categoryInput}
|
||||
onChange={(e) => setCategoryInput(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && (e.preventDefault(), handleAddCategory())}
|
||||
placeholder="Add category"
|
||||
list="available-categories"
|
||||
/>
|
||||
<button type="button" onClick={handleAddCategory} className="btn-add-filter">+</button>
|
||||
</div>
|
||||
<datalist id="available-categories">
|
||||
{availableCategories.map(category => (
|
||||
<option key={category} value={category} />
|
||||
))}
|
||||
</datalist>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label>Auto-Add Tags (Optional)</label>
|
||||
<p className="help-text">Recipes with these tags will be automatically added to this cookbook</p>
|
||||
|
||||
@@ -18,17 +18,14 @@ function EditCookbook() {
|
||||
const [coverImageUrl, setCoverImageUrl] = useState('');
|
||||
const [imageUrlInput, setImageUrlInput] = useState('');
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||
const [autoFilterCategories, setAutoFilterCategories] = useState<string[]>([]);
|
||||
const [autoFilterTags, setAutoFilterTags] = useState<string[]>([]);
|
||||
const [autoFilterCookbookTags, setAutoFilterCookbookTags] = useState<string[]>([]);
|
||||
const [cookbookTags, setCookbookTags] = useState<string[]>([]);
|
||||
|
||||
const [categoryInput, setCategoryInput] = useState('');
|
||||
const [tagInput, setTagInput] = useState('');
|
||||
const [cookbookTagInput, setCookbookTagInput] = useState('');
|
||||
const [cookbookTagFilterInput, setCookbookTagFilterInput] = useState('');
|
||||
const [availableTags, setAvailableTags] = useState<Tag[]>([]);
|
||||
const [availableCategories, setAvailableCategories] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
@@ -49,7 +46,6 @@ function EditCookbook() {
|
||||
setName(cookbook.name);
|
||||
setDescription(cookbook.description || '');
|
||||
setCoverImageUrl(cookbook.coverImageUrl || '');
|
||||
setAutoFilterCategories(cookbook.autoFilterCategories || []);
|
||||
setAutoFilterTags(cookbook.autoFilterTags || []);
|
||||
setAutoFilterCookbookTags(cookbook.autoFilterCookbookTags || []);
|
||||
setCookbookTags(cookbook.tags || []);
|
||||
@@ -57,17 +53,6 @@ function EditCookbook() {
|
||||
|
||||
setAvailableTags(tagsResponse.data || []);
|
||||
|
||||
// Extract unique categories from cookbook's recipes
|
||||
const categories = new Set<string>();
|
||||
if (cookbook && 'recipes' in cookbook) {
|
||||
(cookbook as any).recipes.forEach((recipe: any) => {
|
||||
if (recipe.categories) {
|
||||
recipe.categories.forEach((cat: string) => categories.add(cat));
|
||||
}
|
||||
});
|
||||
}
|
||||
setAvailableCategories(Array.from(categories).sort());
|
||||
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
console.error('Failed to load cookbook:', err);
|
||||
@@ -91,7 +76,6 @@ function EditCookbook() {
|
||||
name,
|
||||
description: description || undefined,
|
||||
coverImageUrl: coverImageUrl === '' ? '' : (coverImageUrl || undefined),
|
||||
autoFilterCategories,
|
||||
autoFilterTags,
|
||||
autoFilterCookbookTags,
|
||||
tags: cookbookTags
|
||||
@@ -106,18 +90,6 @@ function EditCookbook() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddCategory = () => {
|
||||
const trimmed = categoryInput.trim();
|
||||
if (trimmed && !autoFilterCategories.includes(trimmed)) {
|
||||
setAutoFilterCategories([...autoFilterCategories, trimmed]);
|
||||
setCategoryInput('');
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveCategory = (category: string) => {
|
||||
setAutoFilterCategories(autoFilterCategories.filter(c => c !== category));
|
||||
};
|
||||
|
||||
const handleAddTag = () => {
|
||||
const trimmed = tagInput.trim();
|
||||
if (trimmed && !autoFilterTags.includes(trimmed)) {
|
||||
@@ -265,7 +237,6 @@ function EditCookbook() {
|
||||
name,
|
||||
description: description || undefined,
|
||||
coverImageUrl: '',
|
||||
autoFilterCategories,
|
||||
autoFilterTags
|
||||
});
|
||||
setCoverImageUrl('');
|
||||
@@ -339,39 +310,6 @@ function EditCookbook() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label>Auto-Add Categories</label>
|
||||
<p className="help-text">
|
||||
Recipes with these categories will be automatically added to this cookbook
|
||||
</p>
|
||||
<div className="filter-chips">
|
||||
{autoFilterCategories.map(category => (
|
||||
<span key={category} className="filter-chip">
|
||||
{category}
|
||||
<button type="button" onClick={() => handleRemoveCategory(category)}>×</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="input-with-button">
|
||||
<input
|
||||
type="text"
|
||||
value={categoryInput}
|
||||
onChange={(e) => setCategoryInput(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && (e.preventDefault(), handleAddCategory())}
|
||||
placeholder="Add category"
|
||||
list="available-categories"
|
||||
/>
|
||||
<button type="button" onClick={handleAddCategory} className="btn-add-filter">
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
<datalist id="available-categories">
|
||||
{availableCategories.map(category => (
|
||||
<option key={category} value={category} />
|
||||
))}
|
||||
</datalist>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label>Auto-Add Tags</label>
|
||||
<p className="help-text">
|
||||
|
||||
@@ -17,8 +17,6 @@ function RecipeForm({ initialRecipe, onSubmit, onCancel }: RecipeFormProps) {
|
||||
const [cookTime, setCookTime] = useState(initialRecipe?.cookTime?.toString() || '');
|
||||
const [servings, setServings] = useState(initialRecipe?.servings?.toString() || '');
|
||||
const [cuisine, setCuisine] = useState(initialRecipe?.cuisine || '');
|
||||
const [categories, setCategories] = useState<string[]>(initialRecipe?.categories || []);
|
||||
const [categoryInput, setCategoryInput] = useState('');
|
||||
|
||||
// Image handling
|
||||
const [uploadingImage, setUploadingImage] = useState(false);
|
||||
@@ -277,28 +275,6 @@ function RecipeForm({ initialRecipe, onSubmit, onCancel }: RecipeFormProps) {
|
||||
};
|
||||
|
||||
// Category management
|
||||
const handleAddCategory = () => {
|
||||
const trimmedCategory = categoryInput.trim();
|
||||
if (!trimmedCategory) return;
|
||||
if (categories.includes(trimmedCategory)) {
|
||||
setCategoryInput('');
|
||||
return;
|
||||
}
|
||||
setCategories([...categories, trimmedCategory]);
|
||||
setCategoryInput('');
|
||||
};
|
||||
|
||||
const handleRemoveCategory = (categoryToRemove: string) => {
|
||||
setCategories(categories.filter(cat => cat !== categoryToRemove));
|
||||
};
|
||||
|
||||
const handleCategoryInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
handleAddCategory();
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -309,7 +285,6 @@ function RecipeForm({ initialRecipe, onSubmit, onCancel }: RecipeFormProps) {
|
||||
cookTime: cookTime ? parseInt(cookTime) : undefined,
|
||||
servings: servings ? parseInt(servings) : undefined,
|
||||
cuisine: cuisine || undefined,
|
||||
categories: categories.length > 0 ? categories : undefined,
|
||||
};
|
||||
|
||||
if (useSections) {
|
||||
@@ -399,44 +374,6 @@ function RecipeForm({ initialRecipe, onSubmit, onCancel }: RecipeFormProps) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="categories">Categories</label>
|
||||
<div className="tags-input-container">
|
||||
<div className="tags-list">
|
||||
{categories.map((category) => (
|
||||
<span key={category} className="tag">
|
||||
{category}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveCategory(category)}
|
||||
className="tag-remove"
|
||||
title="Remove category"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="tag-input-row">
|
||||
<input
|
||||
type="text"
|
||||
id="categories"
|
||||
value={categoryInput}
|
||||
onChange={(e) => setCategoryInput(e.target.value)}
|
||||
onKeyDown={handleCategoryInputKeyDown}
|
||||
placeholder="Add a category and press Enter"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleAddCategory}
|
||||
className="btn-add-tag"
|
||||
>
|
||||
Add Category
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Image Upload (only for editing existing recipes) */}
|
||||
{initialRecipe?.id && (
|
||||
<div className="form-group image-upload-section">
|
||||
|
||||
@@ -27,8 +27,6 @@ function UnifiedEditRecipe() {
|
||||
const [cookTime, setCookTime] = useState('');
|
||||
const [servings, setServings] = useState('');
|
||||
const [cuisine, setCuisine] = useState('');
|
||||
const [recipeCategories, setRecipeCategories] = useState<string[]>([]);
|
||||
const [categoryInput, setCategoryInput] = useState('');
|
||||
const [recipeTags, setRecipeTags] = useState<string[]>([]);
|
||||
const [tagInput, setTagInput] = useState('');
|
||||
const [availableTags, setAvailableTags] = useState<Tag[]>([]);
|
||||
@@ -92,7 +90,6 @@ function UnifiedEditRecipe() {
|
||||
setCookTime(loadedRecipe.cookTime?.toString() || '');
|
||||
setServings(loadedRecipe.servings?.toString() || '');
|
||||
setCuisine(loadedRecipe.cuisine || '');
|
||||
setRecipeCategories(loadedRecipe.categories || []);
|
||||
|
||||
// Handle tags - API returns array of {tag: {id, name}} objects, we need string[]
|
||||
const tagNames = (loadedRecipe.tags as any)?.map((t: any) => t.tag?.name || t).filter(Boolean) || [];
|
||||
@@ -482,7 +479,6 @@ function UnifiedEditRecipe() {
|
||||
cookTime: cookTime ? parseInt(cookTime) : undefined,
|
||||
servings: servings ? parseInt(servings) : undefined,
|
||||
cuisine: cuisine || undefined,
|
||||
categories: recipeCategories.length > 0 ? recipeCategories : undefined,
|
||||
tags: recipeTags,
|
||||
};
|
||||
|
||||
@@ -602,33 +598,6 @@ function UnifiedEditRecipe() {
|
||||
navigate(`/recipes/${id}`);
|
||||
};
|
||||
|
||||
// Category management functions
|
||||
const handleAddCategory = (categoryName: string) => {
|
||||
const trimmedCategory = categoryName.trim();
|
||||
if (!trimmedCategory) return;
|
||||
|
||||
if (recipeCategories.includes(trimmedCategory)) {
|
||||
setCategoryInput('');
|
||||
return; // Category already exists
|
||||
}
|
||||
|
||||
setRecipeCategories([...recipeCategories, trimmedCategory]);
|
||||
setCategoryInput('');
|
||||
setHasChanges(true);
|
||||
};
|
||||
|
||||
const handleRemoveCategory = (categoryToRemove: string) => {
|
||||
setRecipeCategories(recipeCategories.filter(cat => cat !== categoryToRemove));
|
||||
setHasChanges(true);
|
||||
};
|
||||
|
||||
const handleCategoryInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
handleAddCategory(categoryInput);
|
||||
}
|
||||
};
|
||||
|
||||
// Tag management functions
|
||||
const handleAddTag = async (tagName: string) => {
|
||||
const trimmedTag = tagName.trim();
|
||||
@@ -840,45 +809,6 @@ function UnifiedEditRecipe() {
|
||||
|
||||
</div>
|
||||
|
||||
{/* Categories */}
|
||||
<div className="form-group">
|
||||
<label htmlFor="categories">Categories</label>
|
||||
<div className="tags-input-container">
|
||||
<div className="tags-list">
|
||||
{recipeCategories.map((category) => (
|
||||
<span key={category} className="tag">
|
||||
{category}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveCategory(category)}
|
||||
className="tag-remove"
|
||||
title="Remove category"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="tag-input-row">
|
||||
<input
|
||||
type="text"
|
||||
id="categories"
|
||||
value={categoryInput}
|
||||
onChange={(e) => setCategoryInput(e.target.value)}
|
||||
onKeyDown={handleCategoryInputKeyDown}
|
||||
placeholder="Add a category and press Enter"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleAddCategory(categoryInput)}
|
||||
className="btn-add-tag"
|
||||
>
|
||||
Add Category
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tags */}
|
||||
<div className="form-group">
|
||||
<label htmlFor="tags">Tags</label>
|
||||
|
||||
Reference in New Issue
Block a user