import { computed, reactive, ref, watch } from 'vue' import { useAwardsStore } from '../../stores/awards' import type { AdminCategoryItem } from '../../types/awards' type CategoryStatusFilter = 'all' | 'empty' | 'reviews' | 'thin' interface CategoryForm { groupName: string name: string slug: string description: string sortOrder: number maxNomineesPerUser: number } export function useAdminCategoryManager() { const store = useAwardsStore() const selectedSeasonId = computed(() => store.adminSelectedSeasonId) const seasonDetail = computed(() => store.adminSeasonDetail) const query = ref('') const statusFilter = ref('all') const selectedCategoryId = ref(null) const saving = ref(null) const adminMessage = ref('') const adminError = ref('') const categoryToDelete = ref(null) const deleting = ref(false) const editForms = reactive>({}) const newCategoryForm = reactive(createEmptyCategoryForm()) const categoriesWithState = computed(() => seasonDetail.value.categories .map((category) => ({ ...category, pending: seasonDetail.value.pendingNominations.filter((nomination) => nomination.categoryId === category.id).length, candidates: seasonDetail.value.candidates.filter((candidate) => candidate.categoryId === category.id).length, })) .sort((a, b) => a.sortOrder - b.sortOrder), ) const filteredCategories = computed(() => { const search = query.value.trim().toLowerCase() return categoriesWithState.value.filter((category) => { const matchesStatus = statusFilter.value === 'all' || (statusFilter.value === 'empty' && category.candidates === 0) || (statusFilter.value === 'reviews' && category.pending > 0) || (statusFilter.value === 'thin' && category.candidates > 0 && category.candidates < Math.max(2, category.maxNomineesPerUser)) const matchesSearch = !search || [category.groupName, category.name, category.slug, category.description] .join(' ') .toLowerCase() .includes(search) return matchesStatus && matchesSearch }) }) const selectedCategory = computed(() => filteredCategories.value.find((category) => category.id === selectedCategoryId.value) ?? filteredCategories.value[0] ?? null, ) const categoryStats = computed(() => [ { label: 'Kategorien', value: seasonDetail.value.categories.length }, { label: 'Kandidaten', value: seasonDetail.value.candidates.length }, { label: 'Reviews', value: seasonDetail.value.pendingNominations.length }, ]) const statusFilters = computed(() => [ { key: 'all' as const, label: 'Alle', count: categoriesWithState.value.length }, { key: 'empty' as const, label: 'Ohne Kandidaten', count: categoriesWithState.value.filter((category) => category.candidates === 0).length }, { key: 'reviews' as const, label: 'Mit Reviews', count: categoriesWithState.value.filter((category) => category.pending > 0).length }, { key: 'thin' as const, label: 'Dünn besetzt', count: categoriesWithState.value.filter((category) => category.candidates > 0 && category.candidates < Math.max(2, category.maxNomineesPerUser)).length }, ]) watch( seasonDetail, (detail) => { for (const category of detail.categories) { editForms[category.id] = createCategoryForm(category) } newCategoryForm.sortOrder = detail.categories.length + 1 if (!detail.categories.some((category) => category.id === selectedCategoryId.value)) { selectedCategoryId.value = detail.categories[0]?.id ?? null } }, { immediate: true }, ) watch(filteredCategories, (categories) => { if (!categories.some((category) => category.id === selectedCategoryId.value)) { selectedCategoryId.value = categories[0]?.id ?? null } }) async function saveCategory(categoryId: number) { if (!selectedSeasonId.value) return saving.value = categoryId adminMessage.value = '' adminError.value = '' try { await store.updateAdminCategory(categoryId, selectedSeasonId.value, editForms[categoryId]) adminMessage.value = 'Kategorie gespeichert.' } catch (error) { adminError.value = error instanceof Error ? error.message : 'Kategorie konnte nicht gespeichert werden.' } finally { saving.value = null } } async function createCategory() { if (!selectedSeasonId.value) return saving.value = 'new' adminMessage.value = '' adminError.value = '' try { await store.createAdminCategory(selectedSeasonId.value, newCategoryForm) adminMessage.value = 'Kategorie angelegt.' Object.assign(newCategoryForm, createEmptyCategoryForm(seasonDetail.value.categories.length + 1)) } catch (error) { adminError.value = error instanceof Error ? error.message : 'Kategorie konnte nicht angelegt werden.' } finally { saving.value = null } } function fillNewSlug() { newCategoryForm.slug = slugify(newCategoryForm.name) } async function confirmDeleteCategory() { if (!categoryToDelete.value || !selectedSeasonId.value) return deleting.value = true adminError.value = '' try { await store.deleteAdminCategory(categoryToDelete.value.id, selectedSeasonId.value) adminMessage.value = `Kategorie „${categoryToDelete.value.name}" wurde gelöscht.` categoryToDelete.value = null } catch (error) { adminError.value = error instanceof Error ? error.message : 'Löschen fehlgeschlagen.' } finally { deleting.value = false } } return { selectedSeasonId, query, statusFilter, selectedCategoryId, saving, adminMessage, adminError, editForms, newCategoryForm, filteredCategories, selectedCategory, categoryStats, statusFilters, categoryToDelete, deleting, saveCategory, createCategory, fillNewSlug, confirmDeleteCategory, } } function createCategoryForm(category: AdminCategoryItem): CategoryForm function createCategoryForm(category?: AdminCategoryItem, sortOrder?: number): CategoryForm function createCategoryForm(category?: AdminCategoryItem, sortOrder = 1): CategoryForm { return { groupName: category?.groupName ?? '', name: category?.name ?? '', slug: category?.slug ?? '', description: category?.description ?? '', sortOrder: category?.sortOrder ?? sortOrder, maxNomineesPerUser: category?.maxNomineesPerUser ?? 3, } } function createEmptyCategoryForm(sortOrder = 1): CategoryForm { return createCategoryForm(undefined, sortOrder) } function slugify(value: string) { return value .trim() .toLowerCase() .normalize('NFD') .replace(/[\u0300-\u036f]/g, '') .replace(/[^a-z0-9]+/g, '-') .replace(/^-+|-+$/g, '') }