157 lines
3.7 KiB
TypeScript
157 lines
3.7 KiB
TypeScript
/**
|
|
* TDD Phase 2: GREEN - Minimal code to pass tests
|
|
*
|
|
* This Hook implements works search functionality:
|
|
* - Supports keyword search
|
|
* - Supports category filter
|
|
* - Supports pagination
|
|
* - Only executes query when keyword is provided
|
|
*/
|
|
|
|
import { root } from '@repo/core'
|
|
import { TemplateGenerationController } from '@repo/sdk'
|
|
import { useCallback, useEffect, useState } from 'react'
|
|
|
|
import { type ApiError } from '@/lib/types'
|
|
|
|
import { handleError } from './use-error'
|
|
|
|
// Type definitions
|
|
export type WorksCategory = '全部' | '萌宠' | '写真' | '合拍'
|
|
|
|
export interface UseWorksSearchParams {
|
|
keyword: string
|
|
category?: WorksCategory
|
|
page?: number
|
|
limit?: number
|
|
}
|
|
|
|
export interface WorksSearchResult {
|
|
id: string
|
|
createdAt: Date
|
|
template: {
|
|
id: string
|
|
name: string
|
|
}
|
|
status: string
|
|
duration: number
|
|
}
|
|
|
|
export interface WorksSearchResponse {
|
|
data: WorksSearchResult[]
|
|
total: number
|
|
page: number
|
|
limit: number
|
|
totalPages: number
|
|
}
|
|
|
|
/**
|
|
* Hook for searching works with keyword and category filtering
|
|
*
|
|
* @param params - Search parameters including keyword, category, page, and limit
|
|
* @returns Object with data, loading state, error, and refetch function
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* const { data, works, isLoading, error } = useWorksSearch({
|
|
* keyword: '测试',
|
|
* category: '萌宠',
|
|
* page: 1,
|
|
* limit: 20
|
|
* })
|
|
* ```
|
|
*/
|
|
export function useWorksSearch(params: UseWorksSearchParams) {
|
|
const [loading, setLoading] = useState(false)
|
|
const [error, setError] = useState<ApiError | null>(null)
|
|
const [data, setData] = useState<WorksSearchResponse | undefined>(undefined)
|
|
|
|
const { keyword, category, page = 1, limit = 20 } = params
|
|
|
|
// Auto-execute search when params change
|
|
useEffect(() => {
|
|
const execute = async () => {
|
|
// Only execute query when keyword has content
|
|
const trimmedKeyword = keyword?.trim() || ''
|
|
if (!trimmedKeyword) {
|
|
setData(undefined)
|
|
setError(null)
|
|
return
|
|
}
|
|
|
|
setLoading(true)
|
|
setError(null)
|
|
|
|
// Build API params - only include category if it's not "全部"
|
|
const apiParams = {
|
|
keyword: trimmedKeyword,
|
|
...(category && category !== '全部' && { category }),
|
|
page,
|
|
limit,
|
|
}
|
|
|
|
const templateGeneration = root.get(TemplateGenerationController)
|
|
const { data: result, error: err } = await handleError(async () =>
|
|
await templateGeneration.list(apiParams)
|
|
)
|
|
|
|
if (err) {
|
|
setError(err)
|
|
setLoading(false)
|
|
return
|
|
}
|
|
|
|
setData(result as WorksSearchResponse)
|
|
setLoading(false)
|
|
}
|
|
|
|
execute()
|
|
}, [keyword, category, page, limit])
|
|
|
|
const refetch = useCallback(() => {
|
|
const execute = async () => {
|
|
const trimmedKeyword = keyword?.trim() || ''
|
|
if (!trimmedKeyword) {
|
|
setData(undefined)
|
|
setError(null)
|
|
return { data: undefined, error: null }
|
|
}
|
|
|
|
setLoading(true)
|
|
setError(null)
|
|
|
|
const apiParams = {
|
|
keyword: trimmedKeyword,
|
|
...(category && category !== '全部' && { category }),
|
|
page,
|
|
limit,
|
|
}
|
|
|
|
const templateGeneration = root.get(TemplateGenerationController)
|
|
const { data: result, error: err } = await handleError(async () =>
|
|
await templateGeneration.list(apiParams)
|
|
)
|
|
|
|
if (err) {
|
|
setError(err)
|
|
setLoading(false)
|
|
return { data: undefined, error: err }
|
|
}
|
|
|
|
setData(result as WorksSearchResponse)
|
|
setLoading(false)
|
|
return { data: result as WorksSearchResponse, error: null }
|
|
}
|
|
|
|
return execute()
|
|
}, [keyword, category, page, limit])
|
|
|
|
return {
|
|
data,
|
|
works: data?.data || [],
|
|
isLoading: loading,
|
|
error,
|
|
refetch,
|
|
}
|
|
}
|