mirror of
https://github.com/saltbo/zpan.git
synced 2026-10-29 18:05:37 +08:00
fix: change search from client-side to server-side global search
- Backend: add `search` query param to GET /api/objects, uses LIKE %keyword% across all folders - Frontend: 300ms debounce before sending search request to backend - Search is global — finds files in any directory, not just current folder Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
3a3364b9de
commit
741b4afc1e
@@ -85,11 +85,12 @@ const app = new Hono<Env>()
|
||||
const parent = c.req.query('path') ?? c.req.query('parent') ?? ''
|
||||
const status = c.req.query('status') ?? 'active'
|
||||
const typeFilter = c.req.query('type')
|
||||
const search = c.req.query('search')
|
||||
const page = Number(c.req.query('page') ?? '1')
|
||||
const pageSize = Number(c.req.query('pageSize') ?? '20')
|
||||
|
||||
const db = c.get('platform').db
|
||||
const result = await listMatters(db, orgId, { parent, status, typeFilter, page, pageSize })
|
||||
const result = await listMatters(db, orgId, { parent, status, typeFilter, search, page, pageSize })
|
||||
return c.json(result)
|
||||
})
|
||||
.post('/', zValidator('json', createMatterSchema), async (c) => {
|
||||
|
||||
@@ -68,6 +68,7 @@ interface ListFilters {
|
||||
page: number
|
||||
pageSize: number
|
||||
typeFilter?: string
|
||||
search?: string
|
||||
}
|
||||
|
||||
export async function listMatters(
|
||||
@@ -78,7 +79,9 @@ export async function listMatters(
|
||||
const offset = (filters.page - 1) * filters.pageSize
|
||||
const conditions = [eq(matters.orgId, orgId), eq(matters.status, filters.status)]
|
||||
const typeCond = filters.typeFilter ? typeFilterCondition(filters.typeFilter) : undefined
|
||||
if (typeCond) {
|
||||
if (filters.search) {
|
||||
conditions.push(like(matters.name, `%${filters.search}%`))
|
||||
} else if (typeCond) {
|
||||
conditions.push(typeCond)
|
||||
conditions.push(eq(matters.dirtype, DirType.FILE))
|
||||
} else {
|
||||
|
||||
@@ -79,15 +79,16 @@ export function FileManager({ initialPath, filterType }: FileManagerProps) {
|
||||
const [previewFile, setPreviewFile] = useState<PreviewFile | null>(null)
|
||||
const [previewOpen, setPreviewOpen] = useState(false)
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const [debouncedSearch, setDebouncedSearch] = useState('')
|
||||
|
||||
const query = useFilesQuery(currentPath, filterType)
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => setDebouncedSearch(searchQuery), 300)
|
||||
return () => clearTimeout(timer)
|
||||
}, [searchQuery])
|
||||
|
||||
const query = useFilesQuery(currentPath, filterType, debouncedSearch || undefined)
|
||||
const mutations = useFileMutations(currentPath)
|
||||
const allItems = query.data?.items ?? []
|
||||
const items = useMemo(
|
||||
() =>
|
||||
searchQuery ? allItems.filter((item) => item.name.toLowerCase().includes(searchQuery.toLowerCase())) : allItems,
|
||||
[allItems, searchQuery],
|
||||
)
|
||||
const items = query.data?.items ?? []
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: clear selection and search when path changes
|
||||
useEffect(() => {
|
||||
|
||||
@@ -3,9 +3,9 @@ import { listObjectsByPath } from '@/lib/api'
|
||||
|
||||
const FILES_PAGE_SIZE = 500
|
||||
|
||||
export function useFilesQuery(path: string, typeFilter?: string) {
|
||||
export function useFilesQuery(path: string, typeFilter?: string, search?: string) {
|
||||
return useQuery({
|
||||
queryKey: ['objects', 'active', 'path', path, typeFilter ?? ''],
|
||||
queryFn: () => listObjectsByPath(path, 'active', 1, FILES_PAGE_SIZE, typeFilter),
|
||||
queryKey: ['objects', 'active', 'path', path, typeFilter ?? '', search ?? ''],
|
||||
queryFn: () => listObjectsByPath(path, 'active', 1, FILES_PAGE_SIZE, { type: typeFilter, search }),
|
||||
})
|
||||
}
|
||||
|
||||
+9
-2
@@ -21,9 +21,16 @@ export function listObjects(parent: string, status = 'active', page = 1, pageSiz
|
||||
)
|
||||
}
|
||||
|
||||
export function listObjectsByPath(path: string, status = 'active', page = 1, pageSize = 500, type?: string) {
|
||||
export function listObjectsByPath(
|
||||
path: string,
|
||||
status = 'active',
|
||||
page = 1,
|
||||
pageSize = 500,
|
||||
opts?: { type?: string; search?: string },
|
||||
) {
|
||||
const query: Record<string, string> = { path, status, page: String(page), pageSize: String(pageSize) }
|
||||
if (type) query.type = type
|
||||
if (opts?.type) query.type = opts.type
|
||||
if (opts?.search) query.search = opts.search
|
||||
return unwrap<PaginatedResponse<StorageObject>>(objects.index.$get({ query }))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user