diff --git a/src/components/admin/ColoredItemCombobox.tsx b/src/components/admin/ColoredItemCombobox.tsx index 3d187b3..41fea37 100644 --- a/src/components/admin/ColoredItemCombobox.tsx +++ b/src/components/admin/ColoredItemCombobox.tsx @@ -25,6 +25,7 @@ interface ColoredItemComboboxProps { value: ColoredItem | null; onChange: (item: ColoredItem | null) => void; onCreateNew: (name: string, color: string) => Promise; + onDelete?: (item: ColoredItem) => Promise; placeholder?: string; isLoading?: boolean; colors?: string[]; @@ -36,6 +37,7 @@ export function ColoredItemCombobox({ value, onChange, onCreateNew, + onDelete, placeholder, isLoading = false, colors = DEFAULT_COLORS, @@ -48,6 +50,7 @@ export function ColoredItemCombobox({ const [search, setSearch] = useState(''); const [newColor, setNewColor] = useState(colors[0] ?? '#00e5a0'); const [isCreating, setIsCreating] = useState(false); + const [deletingId, setDeletingId] = useState(null); const containerRef = useRef(null); const searchInputRef = useRef(null); @@ -137,6 +140,30 @@ export function ColoredItemCombobox({ } }, [search, newColor, isCreating, onCreateNew, onChange, haptic]); + const handleDelete = useCallback( + async (e: React.MouseEvent, item: ColoredItem) => { + e.stopPropagation(); + if (!onDelete || deletingId !== null) return; + + haptic.buttonPress(); + setDeletingId(item.id); + + try { + await onDelete(item); + haptic.success(); + // Clear selection if deleted item was selected + if (value?.id === item.id) { + onChange(null); + } + } catch { + haptic.error(); + } finally { + setDeletingId(null); + } + }, + [onDelete, deletingId, value, onChange, haptic], + ); + // Keyboard handling const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { @@ -257,6 +284,23 @@ export function ColoredItemCombobox({ )} + {onDelete && ( + + )} ))} diff --git a/src/locales/en.json b/src/locales/en.json index 8aa7b7d..c2241f4 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -4522,7 +4522,8 @@ "searchOrCreate": "Search or create...", "noItems": "Nothing found", "placeholder": "Select...", - "clear": "Clear" + "clear": "Clear", + "delete": "Delete {{name}}" }, "toolbar": { "bold": "Bold", diff --git a/src/locales/ru.json b/src/locales/ru.json index a257abb..a2c095d 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -5089,7 +5089,8 @@ "searchOrCreate": "Поиск или создание...", "noItems": "Ничего не найдено", "placeholder": "Выберите...", - "clear": "Очистить" + "clear": "Очистить", + "delete": "Удалить {{name}}" }, "toolbar": { "bold": "Жирный", diff --git a/src/pages/AdminNewsCreate.tsx b/src/pages/AdminNewsCreate.tsx index 3dd31b0..b132cab 100644 --- a/src/pages/AdminNewsCreate.tsx +++ b/src/pages/AdminNewsCreate.tsx @@ -445,6 +445,22 @@ export default function AdminNewsCreate() { [queryClient], ); + const handleDeleteCategory = useCallback( + async (item: { id: number }) => { + await newsApi.deleteCategory(item.id); + await queryClient.invalidateQueries({ queryKey: ['admin', 'news', 'categories'] }); + }, + [queryClient], + ); + + const handleDeleteTag = useCallback( + async (item: { id: number }) => { + await newsApi.deleteTag(item.id); + await queryClient.invalidateQueries({ queryKey: ['admin', 'news', 'tags'] }); + }, + [queryClient], + ); + // Fetch article for editing const { data: articleData, isLoading: isLoadingArticle } = useQuery({ queryKey: ['admin', 'news', 'article', articleId], @@ -626,6 +642,7 @@ export default function AdminNewsCreate() { value={selectedCategory} onChange={setSelectedCategory} onCreateNew={handleCreateCategory} + onDelete={handleDeleteCategory} placeholder={t('news.admin.combobox.selectCategory')} /> @@ -636,6 +653,7 @@ export default function AdminNewsCreate() { value={selectedTag} onChange={setSelectedTag} onCreateNew={handleCreateTag} + onDelete={handleDeleteTag} placeholder={t('news.admin.combobox.selectTag')} />