293 lines
8.7 KiB
TypeScript
293 lines
8.7 KiB
TypeScript
import { adminApiClient } from './client'
|
|
import type { EditCardPackDto, CardPackPreviewDto, PaginatedResponse } from '@/types/models'
|
|
import type { AxiosError } from 'axios'
|
|
|
|
export interface PacksApiError {
|
|
message: string
|
|
statusCode?: number
|
|
field?: string
|
|
originalError?: unknown
|
|
name: 'PacksApiError'
|
|
}
|
|
|
|
export function createPacksApiError(
|
|
message: string,
|
|
statusCode?: number,
|
|
field?: string,
|
|
originalError?: unknown
|
|
): PacksApiError {
|
|
return {
|
|
message,
|
|
statusCode,
|
|
field,
|
|
originalError,
|
|
name: 'PacksApiError',
|
|
}
|
|
}
|
|
|
|
export function isPacksApiError(error: unknown): error is PacksApiError {
|
|
return (
|
|
typeof error === 'object' &&
|
|
error !== null &&
|
|
'name' in error &&
|
|
error.name === 'PacksApiError'
|
|
)
|
|
}
|
|
|
|
export const packsApi = {
|
|
// Get all packs with pagination and search
|
|
getPacks: async (params?: {
|
|
page?: number
|
|
limit?: number
|
|
search?: string
|
|
showDisabled?: boolean
|
|
}): Promise<PaginatedResponse<CardPackPreviewDto>> => {
|
|
try {
|
|
const response = await adminApiClient.get('/api/admin/packs', {
|
|
params: {
|
|
page: params?.page || 1,
|
|
limit: params?.limit || 20,
|
|
search: params?.search,
|
|
isPublic: params?.showDisabled === false ? true : undefined,
|
|
},
|
|
})
|
|
// Transform backend response to match frontend PaginatedResponse
|
|
const backendData = response.data
|
|
return {
|
|
items: backendData.packs.map((pack: any) => ({
|
|
id: pack.id,
|
|
title: pack.name,
|
|
cards: pack.questionCount,
|
|
enabled: pack.isPublic,
|
|
})),
|
|
total: backendData.total,
|
|
page: backendData.page,
|
|
limit: backendData.limit,
|
|
totalPages: backendData.totalPages,
|
|
}
|
|
} catch (error) {
|
|
const axiosError = error as AxiosError<{ error?: string; message?: string; field?: string }>
|
|
|
|
// Check for validation errors (limit too high, etc.)
|
|
if (axiosError.response?.status === 400) {
|
|
const errorData = axiosError.response.data
|
|
if (errorData?.message?.includes('Limit')) {
|
|
throw createPacksApiError(
|
|
`Invalid limit: ${errorData.message}. Maximum allowed limit is 100.`,
|
|
axiosError.response.status,
|
|
'limit',
|
|
error
|
|
)
|
|
}
|
|
}
|
|
|
|
throw createPacksApiError(
|
|
axiosError.response?.data?.message ||
|
|
axiosError.response?.data?.error ||
|
|
'Failed to load packs',
|
|
axiosError.response?.status,
|
|
axiosError.response?.data?.field,
|
|
error
|
|
)
|
|
}
|
|
},
|
|
|
|
// Get pack details by ID for editing
|
|
getPack: async (packId: string): Promise<EditCardPackDto> => {
|
|
try {
|
|
const response = await adminApiClient.get(`/api/admin/packs/${packId}`)
|
|
const pack = response.data
|
|
// Transform backend response to match EditCardPackDto
|
|
return {
|
|
id: pack.id,
|
|
name: pack.name,
|
|
description: pack.description,
|
|
category: pack.category,
|
|
isPublic: pack.isPublic,
|
|
questions: pack.questions,
|
|
}
|
|
} catch (error) {
|
|
const axiosError = error as AxiosError<{ error?: string; message?: string }>
|
|
|
|
if (axiosError.response?.status === 404) {
|
|
throw createPacksApiError(
|
|
`Pack not found: The pack with ID "${packId}" does not exist or has been deleted.`,
|
|
axiosError.response.status,
|
|
undefined,
|
|
error
|
|
)
|
|
}
|
|
|
|
throw createPacksApiError(
|
|
axiosError.response?.data?.message ||
|
|
axiosError.response?.data?.error ||
|
|
`Failed to load pack ${packId}`,
|
|
axiosError.response?.status,
|
|
undefined,
|
|
error
|
|
)
|
|
}
|
|
},
|
|
|
|
// Create or update pack
|
|
upsertPack: async (pack: EditCardPackDto): Promise<{ success: boolean; pack: EditCardPackDto }> => {
|
|
try {
|
|
const isUpdate = pack.id && pack.id.length > 0
|
|
const url = isUpdate ? `/api/admin/packs/${pack.id}` : '/api/admin/packs'
|
|
const method = isUpdate ? 'patch' : 'post'
|
|
|
|
const response = await adminApiClient[method](url, {
|
|
name: pack.name,
|
|
description: pack.description,
|
|
category: pack.category,
|
|
isPublic: pack.isPublic,
|
|
questions: pack.questions,
|
|
})
|
|
|
|
return {
|
|
success: true,
|
|
pack: response.data,
|
|
}
|
|
} catch (error) {
|
|
const axiosError = error as AxiosError<{ error?: string; message?: string; field?: string; details?: string }>
|
|
const isUpdate = pack.id && pack.id.length > 0
|
|
const operation = isUpdate ? 'update pack' : 'create pack'
|
|
const baseMessage = axiosError.response?.data?.message ||
|
|
axiosError.response?.data?.error ||
|
|
`Failed to ${operation}`
|
|
|
|
let fullMessage = baseMessage
|
|
if (axiosError.response?.data?.details) {
|
|
fullMessage += `. ${axiosError.response.data.details}`
|
|
}
|
|
|
|
// Special handling for common errors
|
|
if (axiosError.response?.status === 404 && isUpdate) {
|
|
fullMessage = `Pack not found: The pack you're trying to update (ID: ${pack.id}) does not exist.`
|
|
}
|
|
|
|
if (axiosError.response?.data?.field) {
|
|
fullMessage += ` (Field: ${axiosError.response.data.field})`
|
|
}
|
|
|
|
throw createPacksApiError(
|
|
fullMessage,
|
|
axiosError.response?.status,
|
|
axiosError.response?.data?.field,
|
|
error
|
|
)
|
|
}
|
|
},
|
|
|
|
// Delete pack
|
|
deletePack: async (packId: string): Promise<{ success: boolean; message: string }> => {
|
|
try {
|
|
const response = await adminApiClient.delete(`/api/admin/packs/${packId}`)
|
|
return response.data
|
|
} catch (error) {
|
|
const axiosError = error as AxiosError<{ error?: string; message?: string }>
|
|
|
|
if (axiosError.response?.status === 404) {
|
|
throw createPacksApiError(
|
|
`Pack not found: The pack with ID "${packId}" does not exist and cannot be deleted.`,
|
|
axiosError.response.status,
|
|
undefined,
|
|
error
|
|
)
|
|
}
|
|
|
|
throw createPacksApiError(
|
|
axiosError.response?.data?.message ||
|
|
axiosError.response?.data?.error ||
|
|
`Failed to delete pack ${packId}`,
|
|
axiosError.response?.status,
|
|
undefined,
|
|
error
|
|
)
|
|
}
|
|
},
|
|
|
|
// Get empty template for import
|
|
getTemplate: async (): Promise<{
|
|
templateVersion: string
|
|
instructions: string
|
|
questions: Array<{ question: string; answers: Array<{ text: string; points: number }> }>
|
|
}> => {
|
|
try {
|
|
const response = await adminApiClient.get('/api/admin/packs/export/template')
|
|
return response.data
|
|
} catch (error) {
|
|
const axiosError = error as AxiosError<{ error?: string; message?: string }>
|
|
throw createPacksApiError(
|
|
axiosError.response?.data?.message ||
|
|
axiosError.response?.data?.error ||
|
|
'Failed to get template',
|
|
axiosError.response?.status,
|
|
undefined,
|
|
error
|
|
)
|
|
}
|
|
},
|
|
|
|
// Export pack to JSON
|
|
exportPack: async (packId: string): Promise<{
|
|
templateVersion: string
|
|
exportedAt: string
|
|
packInfo: {
|
|
name: string
|
|
description: string
|
|
category: string
|
|
isPublic: boolean
|
|
}
|
|
questions: Array<{ question: string; answers: Array<{ text: string; points: number }> }>
|
|
}> => {
|
|
try {
|
|
const response = await adminApiClient.get(`/api/admin/packs/${packId}/export`)
|
|
return response.data
|
|
} catch (error) {
|
|
const axiosError = error as AxiosError<{ error?: string; message?: string }>
|
|
|
|
if (axiosError.response?.status === 404) {
|
|
throw createPacksApiError(
|
|
`Pack not found: The pack with ID "${packId}" does not exist.`,
|
|
axiosError.response.status,
|
|
undefined,
|
|
error
|
|
)
|
|
}
|
|
|
|
throw createPacksApiError(
|
|
axiosError.response?.data?.message ||
|
|
axiosError.response?.data?.error ||
|
|
`Failed to export pack ${packId}`,
|
|
axiosError.response?.status,
|
|
undefined,
|
|
error
|
|
)
|
|
}
|
|
},
|
|
|
|
// Import pack from JSON
|
|
importPack: async (data: {
|
|
name: string
|
|
description: string
|
|
category: string
|
|
isPublic: boolean
|
|
questions: Array<{ question: string; answers: Array<{ text: string; points: number }> }>
|
|
}): Promise<EditCardPackDto> => {
|
|
try {
|
|
const response = await adminApiClient.post('/api/admin/packs/import', data)
|
|
return response.data
|
|
} catch (error) {
|
|
const axiosError = error as AxiosError<{ error?: string; message?: string }>
|
|
throw createPacksApiError(
|
|
axiosError.response?.data?.message ||
|
|
axiosError.response?.data?.error ||
|
|
'Failed to import pack',
|
|
axiosError.response?.status,
|
|
undefined,
|
|
error
|
|
)
|
|
}
|
|
},
|
|
}
|