feat(categories): tree browser, CRUD dialog, pending approval queue with merge
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
import { api } from '@/api/client'
|
||||
import type {
|
||||
ApiResponse,
|
||||
Category,
|
||||
CategoryTranslation,
|
||||
ListParams,
|
||||
PaginatedResponse,
|
||||
} from '@/api/types'
|
||||
|
||||
const BASE = '/api/admin/v1/categories'
|
||||
|
||||
export interface CategoryPayload {
|
||||
parent_id?: string | null
|
||||
slug?: string
|
||||
sort_order?: number
|
||||
is_active?: boolean
|
||||
translations?: Array<{ locale: string; name: string; description?: string | null }>
|
||||
}
|
||||
|
||||
export function fetchCategoryTree(includeInactive = true) {
|
||||
return api.get<ApiResponse<Category[]>>(`${BASE}/tree`, {
|
||||
include_inactive: includeInactive,
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchPendingCategories(params: ListParams) {
|
||||
return api.get<PaginatedResponse<Category>>(`${BASE}/pending`, params)
|
||||
}
|
||||
|
||||
export function fetchCategory(id: string) {
|
||||
return api.get<ApiResponse<Category>>(`${BASE}/${id}`)
|
||||
}
|
||||
|
||||
export function createCategory(payload: CategoryPayload) {
|
||||
return api.post<ApiResponse<Category>>(BASE, payload)
|
||||
}
|
||||
|
||||
export function updateCategory(id: string, payload: CategoryPayload) {
|
||||
return api.put<ApiResponse<Category>>(`${BASE}/${id}`, payload)
|
||||
}
|
||||
|
||||
export function deleteCategory(id: string) {
|
||||
return api.delete<ApiResponse<null>>(`${BASE}/${id}`)
|
||||
}
|
||||
|
||||
export function approveCategory(id: string) {
|
||||
return api.post<ApiResponse<Category>>(`${BASE}/${id}/approve`)
|
||||
}
|
||||
|
||||
export function rejectCategory(id: string) {
|
||||
return api.post<ApiResponse<Category>>(`${BASE}/${id}/reject`)
|
||||
}
|
||||
|
||||
export function mergeCategory(id: string, targetId: string) {
|
||||
return api.post<ApiResponse<Category>>(`${BASE}/${id}/merge`, { target_id: targetId })
|
||||
}
|
||||
|
||||
export function fetchCategoryTranslations(id: string) {
|
||||
return api.get<ApiResponse<CategoryTranslation[]>>(`${BASE}/${id}/translations`)
|
||||
}
|
||||
|
||||
export function upsertCategoryTranslation(
|
||||
id: string,
|
||||
locale: string,
|
||||
payload: { name: string; description?: string | null },
|
||||
) {
|
||||
return api.post<ApiResponse<CategoryTranslation>>(`${BASE}/${id}/translations/${locale}`, payload)
|
||||
}
|
||||
|
||||
export function deleteCategoryTranslation(id: string, locale: string) {
|
||||
return api.delete<ApiResponse<null>>(`${BASE}/${id}/translations/${locale}`)
|
||||
}
|
||||
|
||||
/** Flatten the tree for parent/merge-target pickers. */
|
||||
export function flattenTree(nodes: Category[], depth = 0): Array<Category & { depth: number }> {
|
||||
return nodes.flatMap((node) => [
|
||||
{ ...node, depth },
|
||||
...flattenTree(node.children ?? [], depth + 1),
|
||||
])
|
||||
}
|
||||
Reference in New Issue
Block a user