feat: add global Komga search autocomplete in header
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 5m50s

This commit is contained in:
2026-03-04 13:46:02 +01:00
parent 818fe67c99
commit 3d7ac0c13e
6 changed files with 411 additions and 9 deletions

View File

@@ -0,0 +1,63 @@
import type { KomgaBook, KomgaSeries } from "@/types/komga";
import { ERROR_CODES } from "../../constants/errorCodes";
import { AppError } from "../../utils/errors";
import { BaseApiService } from "./base-api.service";
interface SearchResponse<T> {
content: T[];
}
export interface GlobalSearchResult {
series: KomgaSeries[];
books: KomgaBook[];
}
export class SearchService extends BaseApiService {
private static readonly CACHE_TTL = 30;
static async globalSearch(query: string, limit: number = 6): Promise<GlobalSearchResult> {
const trimmedQuery = query.trim();
if (!trimmedQuery) {
return { series: [], books: [] };
}
const headers = { "Content-Type": "application/json" };
const searchBody = {
fullTextSearch: trimmedQuery,
};
try {
const [seriesResponse, booksResponse] = await Promise.all([
this.fetchFromApi<SearchResponse<KomgaSeries>>(
{ path: "series/list", params: { page: "0", size: String(limit) } },
headers,
{
method: "POST",
body: JSON.stringify(searchBody),
revalidate: this.CACHE_TTL,
}
),
this.fetchFromApi<SearchResponse<KomgaBook>>(
{ path: "books/list", params: { page: "0", size: String(limit) } },
headers,
{
method: "POST",
body: JSON.stringify(searchBody),
revalidate: this.CACHE_TTL,
}
),
]);
return {
series: seriesResponse.content.filter((item) => !item.deleted),
books: booksResponse.content.filter((item) => !item.deleted),
};
} catch (error) {
if (error instanceof AppError) {
throw error;
}
throw new AppError(ERROR_CODES.SERIES.FETCH_ERROR, {}, error);
}
}
}