Add multi-select machine filters
Replace machine dropdowns with multi-select chips and pass machine lists in queries. Signed-off-by: codex@lucy.xalior.com
This commit is contained in:
@@ -9,7 +9,7 @@ const querySchema = z.object({
|
|||||||
year: z.coerce.number().int().optional(),
|
year: z.coerce.number().int().optional(),
|
||||||
sort: z.enum(["year_desc", "year_asc", "title", "entry_id_desc"]).optional(),
|
sort: z.enum(["year_desc", "year_asc", "title", "entry_id_desc"]).optional(),
|
||||||
dLanguageId: z.string().trim().length(2).optional(),
|
dLanguageId: z.string().trim().length(2).optional(),
|
||||||
dMachinetypeId: z.coerce.number().int().positive().optional(),
|
dMachinetypeId: z.string().optional(),
|
||||||
filetypeId: z.coerce.number().int().positive().optional(),
|
filetypeId: z.coerce.number().int().positive().optional(),
|
||||||
schemetypeId: z.string().trim().length(2).optional(),
|
schemetypeId: z.string().trim().length(2).optional(),
|
||||||
sourcetypeId: z.string().trim().length(1).optional(),
|
sourcetypeId: z.string().trim().length(1).optional(),
|
||||||
@@ -17,6 +17,15 @@ const querySchema = z.object({
|
|||||||
isDemo: z.coerce.boolean().optional(),
|
isDemo: z.coerce.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function parseIdList(value: string | undefined) {
|
||||||
|
if (!value) return undefined;
|
||||||
|
const ids = value
|
||||||
|
.split(",")
|
||||||
|
.map((id) => Number(id.trim()))
|
||||||
|
.filter((id) => Number.isFinite(id) && id > 0);
|
||||||
|
return ids.length ? ids : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export async function GET(req: NextRequest) {
|
export async function GET(req: NextRequest) {
|
||||||
const { searchParams } = new URL(req.url);
|
const { searchParams } = new URL(req.url);
|
||||||
const parsed = querySchema.safeParse({
|
const parsed = querySchema.safeParse({
|
||||||
@@ -39,7 +48,8 @@ export async function GET(req: NextRequest) {
|
|||||||
headers: { "content-type": "application/json" },
|
headers: { "content-type": "application/json" },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const data = await searchReleases(parsed.data);
|
const dMachinetypeId = parseIdList(parsed.data.dMachinetypeId);
|
||||||
|
const data = await searchReleases({ ...parsed.data, dMachinetypeId });
|
||||||
return new Response(JSON.stringify(data), {
|
return new Response(JSON.stringify(data), {
|
||||||
headers: { "content-type": "application/json" },
|
headers: { "content-type": "application/json" },
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,12 +12,21 @@ const querySchema = z.object({
|
|||||||
.trim()
|
.trim()
|
||||||
.length(2, "languageId must be a 2-char code")
|
.length(2, "languageId must be a 2-char code")
|
||||||
.optional(),
|
.optional(),
|
||||||
machinetypeId: z.coerce.number().int().positive().optional(),
|
machinetypeId: z.string().optional(),
|
||||||
sort: z.enum(["title", "id_desc"]).optional(),
|
sort: z.enum(["title", "id_desc"]).optional(),
|
||||||
scope: z.enum(["title", "title_aliases", "title_aliases_origins"]).optional(),
|
scope: z.enum(["title", "title_aliases", "title_aliases_origins"]).optional(),
|
||||||
facets: z.coerce.boolean().optional(),
|
facets: z.coerce.boolean().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function parseIdList(value: string | undefined) {
|
||||||
|
if (!value) return undefined;
|
||||||
|
const ids = value
|
||||||
|
.split(",")
|
||||||
|
.map((id) => Number(id.trim()))
|
||||||
|
.filter((id) => Number.isFinite(id) && id > 0);
|
||||||
|
return ids.length ? ids : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export async function GET(req: NextRequest) {
|
export async function GET(req: NextRequest) {
|
||||||
const { searchParams } = new URL(req.url);
|
const { searchParams } = new URL(req.url);
|
||||||
const parsed = querySchema.safeParse({
|
const parsed = querySchema.safeParse({
|
||||||
@@ -37,9 +46,11 @@ export async function GET(req: NextRequest) {
|
|||||||
{ status: 400, headers: { "content-type": "application/json" } }
|
{ status: 400, headers: { "content-type": "application/json" } }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const data = await searchEntries(parsed.data);
|
const machinetypeId = parseIdList(parsed.data.machinetypeId);
|
||||||
|
const searchParamsParsed = { ...parsed.data, machinetypeId };
|
||||||
|
const data = await searchEntries(searchParamsParsed);
|
||||||
const body = parsed.data.facets
|
const body = parsed.data.facets
|
||||||
? { ...data, facets: await getEntryFacets(parsed.data) }
|
? { ...data, facets: await getEntryFacets(searchParamsParsed) }
|
||||||
: data;
|
: data;
|
||||||
return new Response(JSON.stringify(body), {
|
return new Response(JSON.stringify(body), {
|
||||||
headers: { "content-type": "application/json" },
|
headers: { "content-type": "application/json" },
|
||||||
|
|||||||
@@ -52,11 +52,21 @@ export default function EntriesExplorer({
|
|||||||
page: number;
|
page: number;
|
||||||
genreId: string | number | "";
|
genreId: string | number | "";
|
||||||
languageId: string | "";
|
languageId: string | "";
|
||||||
machinetypeId: string | number | "";
|
machinetypeId: string;
|
||||||
sort: "title" | "id_desc";
|
sort: "title" | "id_desc";
|
||||||
scope?: SearchScope;
|
scope?: SearchScope;
|
||||||
};
|
};
|
||||||
}) {
|
}) {
|
||||||
|
const preferredMachineIds = [27, 26, 8, 9];
|
||||||
|
const parseMachineIds = (value?: string) => {
|
||||||
|
if (!value) return preferredMachineIds.slice();
|
||||||
|
const ids = value
|
||||||
|
.split(",")
|
||||||
|
.map((id) => Number(id.trim()))
|
||||||
|
.filter((id) => Number.isFinite(id) && id > 0);
|
||||||
|
return ids.length ? ids : preferredMachineIds.slice();
|
||||||
|
};
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
|
||||||
@@ -72,17 +82,20 @@ export default function EntriesExplorer({
|
|||||||
initialUrlState?.genreId === "" ? "" : initialUrlState?.genreId ? Number(initialUrlState.genreId) : ""
|
initialUrlState?.genreId === "" ? "" : initialUrlState?.genreId ? Number(initialUrlState.genreId) : ""
|
||||||
);
|
);
|
||||||
const [languageId, setLanguageId] = useState<string | "">(initialUrlState?.languageId ?? "");
|
const [languageId, setLanguageId] = useState<string | "">(initialUrlState?.languageId ?? "");
|
||||||
const [machinetypeId, setMachinetypeId] = useState<number | "">(
|
const [machinetypeIds, setMachinetypeIds] = useState<number[]>(parseMachineIds(initialUrlState?.machinetypeId));
|
||||||
initialUrlState?.machinetypeId === "" ? "" : initialUrlState?.machinetypeId ? Number(initialUrlState.machinetypeId) : ""
|
|
||||||
);
|
|
||||||
const [sort, setSort] = useState<"title" | "id_desc">(initialUrlState?.sort ?? "id_desc");
|
const [sort, setSort] = useState<"title" | "id_desc">(initialUrlState?.sort ?? "id_desc");
|
||||||
const [scope, setScope] = useState<SearchScope>(initialUrlState?.scope ?? "title");
|
const [scope, setScope] = useState<SearchScope>(initialUrlState?.scope ?? "title");
|
||||||
const [facets, setFacets] = useState<EntryFacets | null>(initialFacets ?? null);
|
const [facets, setFacets] = useState<EntryFacets | null>(initialFacets ?? null);
|
||||||
const preferredMachineIds = [27, 26, 8, 9];
|
|
||||||
const preferredMachineNames = useMemo(() => {
|
const preferredMachineNames = useMemo(() => {
|
||||||
if (!machines.length) return preferredMachineIds.map((id) => `#${id}`);
|
if (!machines.length) return preferredMachineIds.map((id) => `#${id}`);
|
||||||
return preferredMachineIds.map((id) => machines.find((m) => m.id === id)?.name ?? `#${id}`);
|
return preferredMachineIds.map((id) => machines.find((m) => m.id === id)?.name ?? `#${id}`);
|
||||||
}, [machines]);
|
}, [machines]);
|
||||||
|
const orderedMachines = useMemo(() => {
|
||||||
|
const seen = new Set(preferredMachineIds);
|
||||||
|
const preferred = preferredMachineIds.map((id) => machines.find((m) => m.id === id)).filter(Boolean) as { id: number; name: string }[];
|
||||||
|
const rest = machines.filter((m) => !seen.has(m.id));
|
||||||
|
return [...preferred, ...rest];
|
||||||
|
}, [machines]);
|
||||||
|
|
||||||
const pageSize = 20;
|
const pageSize = 20;
|
||||||
const totalPages = useMemo(() => (data ? Math.max(1, Math.ceil(data.total / data.pageSize)) : 1), [data]);
|
const totalPages = useMemo(() => (data ? Math.max(1, Math.ceil(data.total / data.pageSize)) : 1), [data]);
|
||||||
@@ -97,14 +110,14 @@ export default function EntriesExplorer({
|
|||||||
const name = languages.find((l) => l.id === languageId)?.name ?? languageId;
|
const name = languages.find((l) => l.id === languageId)?.name ?? languageId;
|
||||||
chips.push(`lang: ${name}`);
|
chips.push(`lang: ${name}`);
|
||||||
}
|
}
|
||||||
if (machinetypeId !== "") {
|
if (machinetypeIds.length > 0) {
|
||||||
const name = machines.find((m) => m.id === Number(machinetypeId))?.name ?? `#${machinetypeId}`;
|
const names = machinetypeIds.map((id) => machines.find((m) => m.id === id)?.name ?? `#${id}`);
|
||||||
chips.push(`machine: ${name}`);
|
chips.push(`machine: ${names.join(", ")}`);
|
||||||
}
|
}
|
||||||
if (scope === "title_aliases") chips.push("scope: titles + aliases");
|
if (scope === "title_aliases") chips.push("scope: titles + aliases");
|
||||||
if (scope === "title_aliases_origins") chips.push("scope: titles + aliases + origins");
|
if (scope === "title_aliases_origins") chips.push("scope: titles + aliases + origins");
|
||||||
return chips;
|
return chips;
|
||||||
}, [appliedQ, genreId, languageId, machinetypeId, scope, genres, languages, machines]);
|
}, [appliedQ, genreId, languageId, machinetypeIds, scope, genres, languages, machines]);
|
||||||
|
|
||||||
function updateUrl(nextPage = page) {
|
function updateUrl(nextPage = page) {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
@@ -112,7 +125,7 @@ export default function EntriesExplorer({
|
|||||||
params.set("page", String(nextPage));
|
params.set("page", String(nextPage));
|
||||||
if (genreId !== "") params.set("genreId", String(genreId));
|
if (genreId !== "") params.set("genreId", String(genreId));
|
||||||
if (languageId !== "") params.set("languageId", String(languageId));
|
if (languageId !== "") params.set("languageId", String(languageId));
|
||||||
if (machinetypeId !== "") params.set("machinetypeId", String(machinetypeId));
|
if (machinetypeIds.length > 0) params.set("machinetypeId", machinetypeIds.join(","));
|
||||||
if (sort) params.set("sort", sort);
|
if (sort) params.set("sort", sort);
|
||||||
if (scope !== "title") params.set("scope", scope);
|
if (scope !== "title") params.set("scope", scope);
|
||||||
const qs = params.toString();
|
const qs = params.toString();
|
||||||
@@ -128,7 +141,7 @@ export default function EntriesExplorer({
|
|||||||
params.set("pageSize", String(pageSize));
|
params.set("pageSize", String(pageSize));
|
||||||
if (genreId !== "") params.set("genreId", String(genreId));
|
if (genreId !== "") params.set("genreId", String(genreId));
|
||||||
if (languageId !== "") params.set("languageId", String(languageId));
|
if (languageId !== "") params.set("languageId", String(languageId));
|
||||||
if (machinetypeId !== "") params.set("machinetypeId", String(machinetypeId));
|
if (machinetypeIds.length > 0) params.set("machinetypeId", machinetypeIds.join(","));
|
||||||
if (sort) params.set("sort", sort);
|
if (sort) params.set("sort", sort);
|
||||||
if (scope !== "title") params.set("scope", scope);
|
if (scope !== "title") params.set("scope", scope);
|
||||||
if (withFacets) params.set("facets", "true");
|
if (withFacets) params.set("facets", "true");
|
||||||
@@ -165,8 +178,7 @@ export default function EntriesExplorer({
|
|||||||
(initialUrlState?.q ?? "") === appliedQ &&
|
(initialUrlState?.q ?? "") === appliedQ &&
|
||||||
(initialUrlState?.genreId === "" ? "" : Number(initialUrlState?.genreId ?? "")) === (genreId === "" ? "" : Number(genreId)) &&
|
(initialUrlState?.genreId === "" ? "" : Number(initialUrlState?.genreId ?? "")) === (genreId === "" ? "" : Number(genreId)) &&
|
||||||
(initialUrlState?.languageId ?? "") === (languageId ?? "") &&
|
(initialUrlState?.languageId ?? "") === (languageId ?? "") &&
|
||||||
(initialUrlState?.machinetypeId === "" ? "" : Number(initialUrlState?.machinetypeId ?? "")) ===
|
parseMachineIds(initialUrlState?.machinetypeId).join(",") === machinetypeIds.join(",") &&
|
||||||
(machinetypeId === "" ? "" : Number(machinetypeId)) &&
|
|
||||||
sort === (initialUrlState?.sort ?? "id_desc") &&
|
sort === (initialUrlState?.sort ?? "id_desc") &&
|
||||||
(initialUrlState?.scope ?? "title") === scope
|
(initialUrlState?.scope ?? "title") === scope
|
||||||
) {
|
) {
|
||||||
@@ -176,7 +188,7 @@ export default function EntriesExplorer({
|
|||||||
updateUrl(page);
|
updateUrl(page);
|
||||||
fetchData(appliedQ, page, true);
|
fetchData(appliedQ, page, true);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [page, genreId, languageId, machinetypeId, sort, scope, appliedQ]);
|
}, [page, genreId, languageId, machinetypeIds, sort, scope, appliedQ]);
|
||||||
|
|
||||||
// Load filter lists on mount only if not provided by server
|
// Load filter lists on mount only if not provided by server
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -207,7 +219,7 @@ export default function EntriesExplorer({
|
|||||||
setAppliedQ("");
|
setAppliedQ("");
|
||||||
setGenreId("");
|
setGenreId("");
|
||||||
setLanguageId("");
|
setLanguageId("");
|
||||||
setMachinetypeId("");
|
setMachinetypeIds(preferredMachineIds.slice());
|
||||||
setSort("id_desc");
|
setSort("id_desc");
|
||||||
setScope("title");
|
setScope("title");
|
||||||
setPage(1);
|
setPage(1);
|
||||||
@@ -219,11 +231,11 @@ export default function EntriesExplorer({
|
|||||||
params.set("page", String(Math.max(1, (data?.page ?? 1) - 1)));
|
params.set("page", String(Math.max(1, (data?.page ?? 1) - 1)));
|
||||||
if (genreId !== "") params.set("genreId", String(genreId));
|
if (genreId !== "") params.set("genreId", String(genreId));
|
||||||
if (languageId !== "") params.set("languageId", String(languageId));
|
if (languageId !== "") params.set("languageId", String(languageId));
|
||||||
if (machinetypeId !== "") params.set("machinetypeId", String(machinetypeId));
|
if (machinetypeIds.length > 0) params.set("machinetypeId", machinetypeIds.join(","));
|
||||||
if (sort) params.set("sort", sort);
|
if (sort) params.set("sort", sort);
|
||||||
if (scope !== "title") params.set("scope", scope);
|
if (scope !== "title") params.set("scope", scope);
|
||||||
return `/zxdb/entries?${params.toString()}`;
|
return `/zxdb/entries?${params.toString()}`;
|
||||||
}, [appliedQ, data?.page, genreId, languageId, machinetypeId, sort, scope]);
|
}, [appliedQ, data?.page, genreId, languageId, machinetypeIds, sort, scope]);
|
||||||
|
|
||||||
const nextHref = useMemo(() => {
|
const nextHref = useMemo(() => {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
@@ -231,11 +243,11 @@ export default function EntriesExplorer({
|
|||||||
params.set("page", String(Math.max(1, (data?.page ?? 1) + 1)));
|
params.set("page", String(Math.max(1, (data?.page ?? 1) + 1)));
|
||||||
if (genreId !== "") params.set("genreId", String(genreId));
|
if (genreId !== "") params.set("genreId", String(genreId));
|
||||||
if (languageId !== "") params.set("languageId", String(languageId));
|
if (languageId !== "") params.set("languageId", String(languageId));
|
||||||
if (machinetypeId !== "") params.set("machinetypeId", String(machinetypeId));
|
if (machinetypeIds.length > 0) params.set("machinetypeId", machinetypeIds.join(","));
|
||||||
if (sort) params.set("sort", sort);
|
if (sort) params.set("sort", sort);
|
||||||
if (scope !== "title") params.set("scope", scope);
|
if (scope !== "title") params.set("scope", scope);
|
||||||
return `/zxdb/entries?${params.toString()}`;
|
return `/zxdb/entries?${params.toString()}`;
|
||||||
}, [appliedQ, data?.page, genreId, languageId, machinetypeId, sort, scope]);
|
}, [appliedQ, data?.page, genreId, languageId, machinetypeIds, sort, scope]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -303,15 +315,34 @@ export default function EntriesExplorer({
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="form-label small text-secondary">Machine</label>
|
<label className="form-label small text-secondary">Machine</label>
|
||||||
<select className="form-select" value={machinetypeId} onChange={(e) => { setMachinetypeId(e.target.value === "" ? "" : Number(e.target.value)); setPage(1); }}>
|
<div className="d-flex flex-wrap gap-2">
|
||||||
<option value="">All machines</option>
|
{orderedMachines.map((m) => {
|
||||||
{machines.map((m) => (
|
const active = machinetypeIds.includes(m.id);
|
||||||
<option key={m.id} value={m.id}>{m.name}</option>
|
return (
|
||||||
))}
|
<button
|
||||||
</select>
|
key={m.id}
|
||||||
{machinetypeId === "" && (
|
type="button"
|
||||||
<div className="form-text">Preferred: {preferredMachineNames.join(", ")}</div>
|
className={`btn btn-sm ${active ? "btn-primary" : "btn-outline-secondary"}`}
|
||||||
)}
|
onClick={() => {
|
||||||
|
setMachinetypeIds((current) => {
|
||||||
|
const next = new Set(current);
|
||||||
|
if (next.has(m.id)) {
|
||||||
|
next.delete(m.id);
|
||||||
|
} else {
|
||||||
|
next.add(m.id);
|
||||||
|
}
|
||||||
|
const order = orderedMachines.map((item) => item.id);
|
||||||
|
return order.filter((id) => next.has(id));
|
||||||
|
});
|
||||||
|
setPage(1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{m.name}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className="form-text">Preferred: {preferredMachineNames.join(", ")}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="form-label small text-secondary">Sort</label>
|
<label className="form-label small text-secondary">Sort</label>
|
||||||
|
|||||||
@@ -7,12 +7,24 @@ export const metadata = {
|
|||||||
|
|
||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
|
function parseIdList(value: string | string[] | undefined) {
|
||||||
|
if (!value) return undefined;
|
||||||
|
const raw = Array.isArray(value) ? value.join(",") : value;
|
||||||
|
const ids = raw
|
||||||
|
.split(",")
|
||||||
|
.map((id) => Number(id.trim()))
|
||||||
|
.filter((id) => Number.isFinite(id) && id > 0);
|
||||||
|
return ids.length ? ids : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export default async function Page({ searchParams }: { searchParams: Promise<{ [key: string]: string | string[] | undefined }> }) {
|
export default async function Page({ searchParams }: { searchParams: Promise<{ [key: string]: string | string[] | undefined }> }) {
|
||||||
const sp = await searchParams;
|
const sp = await searchParams;
|
||||||
const page = Math.max(1, Number(Array.isArray(sp.page) ? sp.page[0] : sp.page) || 1);
|
const page = Math.max(1, Number(Array.isArray(sp.page) ? sp.page[0] : sp.page) || 1);
|
||||||
const genreId = (Array.isArray(sp.genreId) ? sp.genreId[0] : sp.genreId) ?? "";
|
const genreId = (Array.isArray(sp.genreId) ? sp.genreId[0] : sp.genreId) ?? "";
|
||||||
const languageId = (Array.isArray(sp.languageId) ? sp.languageId[0] : sp.languageId) ?? "";
|
const languageId = (Array.isArray(sp.languageId) ? sp.languageId[0] : sp.languageId) ?? "";
|
||||||
const machinetypeId = (Array.isArray(sp.machinetypeId) ? sp.machinetypeId[0] : sp.machinetypeId) ?? "";
|
const preferredMachineIds = [27, 26, 8, 9];
|
||||||
|
const machinetypeIds = parseIdList(sp.machinetypeId) ?? preferredMachineIds;
|
||||||
|
const machinetypeId = machinetypeIds.join(",");
|
||||||
const sort = ((Array.isArray(sp.sort) ? sp.sort[0] : sp.sort) ?? "id_desc") as "title" | "id_desc";
|
const sort = ((Array.isArray(sp.sort) ? sp.sort[0] : sp.sort) ?? "id_desc") as "title" | "id_desc";
|
||||||
const q = (Array.isArray(sp.q) ? sp.q[0] : sp.q) ?? "";
|
const q = (Array.isArray(sp.q) ? sp.q[0] : sp.q) ?? "";
|
||||||
const scope = ((Array.isArray(sp.scope) ? sp.scope[0] : sp.scope) ?? "title") as
|
const scope = ((Array.isArray(sp.scope) ? sp.scope[0] : sp.scope) ?? "title") as
|
||||||
@@ -29,7 +41,7 @@ export default async function Page({ searchParams }: { searchParams: Promise<{ [
|
|||||||
scope,
|
scope,
|
||||||
genreId: genreId ? Number(genreId) : undefined,
|
genreId: genreId ? Number(genreId) : undefined,
|
||||||
languageId: languageId || undefined,
|
languageId: languageId || undefined,
|
||||||
machinetypeId: machinetypeId ? Number(machinetypeId) : undefined,
|
machinetypeId: machinetypeIds,
|
||||||
}),
|
}),
|
||||||
listGenres(),
|
listGenres(),
|
||||||
listLanguages(),
|
listLanguages(),
|
||||||
@@ -40,7 +52,7 @@ export default async function Page({ searchParams }: { searchParams: Promise<{ [
|
|||||||
scope,
|
scope,
|
||||||
genreId: genreId ? Number(genreId) : undefined,
|
genreId: genreId ? Number(genreId) : undefined,
|
||||||
languageId: languageId || undefined,
|
languageId: languageId || undefined,
|
||||||
machinetypeId: machinetypeId ? Number(machinetypeId) : undefined,
|
machinetypeId: machinetypeIds,
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,16 @@ export default function ReleasesExplorer({
|
|||||||
casetypes: { id: string; name: string }[];
|
casetypes: { id: string; name: string }[];
|
||||||
};
|
};
|
||||||
}) {
|
}) {
|
||||||
|
const preferredMachineIds = [27, 26, 8, 9];
|
||||||
|
const parseMachineIds = (value?: string) => {
|
||||||
|
if (!value) return preferredMachineIds.slice();
|
||||||
|
const ids = value
|
||||||
|
.split(",")
|
||||||
|
.map((id) => Number(id.trim()))
|
||||||
|
.filter((id) => Number.isFinite(id) && id > 0);
|
||||||
|
return ids.length ? ids : preferredMachineIds.slice();
|
||||||
|
};
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
|
||||||
@@ -64,7 +74,7 @@ export default function ReleasesExplorer({
|
|||||||
|
|
||||||
// Download-based filters and their option lists
|
// Download-based filters and their option lists
|
||||||
const [dLanguageId, setDLanguageId] = useState<string>(initialUrlState?.dLanguageId ?? "");
|
const [dLanguageId, setDLanguageId] = useState<string>(initialUrlState?.dLanguageId ?? "");
|
||||||
const [dMachinetypeId, setDMachinetypeId] = useState<string>(initialUrlState?.dMachinetypeId ?? "");
|
const [dMachinetypeIds, setDMachinetypeIds] = useState<number[]>(parseMachineIds(initialUrlState?.dMachinetypeId));
|
||||||
const [filetypeId, setFiletypeId] = useState<string>(initialUrlState?.filetypeId ?? "");
|
const [filetypeId, setFiletypeId] = useState<string>(initialUrlState?.filetypeId ?? "");
|
||||||
const [schemetypeId, setSchemetypeId] = useState<string>(initialUrlState?.schemetypeId ?? "");
|
const [schemetypeId, setSchemetypeId] = useState<string>(initialUrlState?.schemetypeId ?? "");
|
||||||
const [sourcetypeId, setSourcetypeId] = useState<string>(initialUrlState?.sourcetypeId ?? "");
|
const [sourcetypeId, setSourcetypeId] = useState<string>(initialUrlState?.sourcetypeId ?? "");
|
||||||
@@ -78,11 +88,16 @@ export default function ReleasesExplorer({
|
|||||||
const [sources, setSources] = useState<{ id: string; name: string }[]>(initialLists?.sourcetypes ?? []);
|
const [sources, setSources] = useState<{ id: string; name: string }[]>(initialLists?.sourcetypes ?? []);
|
||||||
const [cases, setCases] = useState<{ id: string; name: string }[]>(initialLists?.casetypes ?? []);
|
const [cases, setCases] = useState<{ id: string; name: string }[]>(initialLists?.casetypes ?? []);
|
||||||
const initialLoad = useRef(true);
|
const initialLoad = useRef(true);
|
||||||
const preferredMachineIds = [27, 26, 8, 9];
|
|
||||||
const preferredMachineNames = useMemo(() => {
|
const preferredMachineNames = useMemo(() => {
|
||||||
if (!machines.length) return preferredMachineIds.map((id) => `#${id}`);
|
if (!machines.length) return preferredMachineIds.map((id) => `#${id}`);
|
||||||
return preferredMachineIds.map((id) => machines.find((m) => m.id === id)?.name ?? `#${id}`);
|
return preferredMachineIds.map((id) => machines.find((m) => m.id === id)?.name ?? `#${id}`);
|
||||||
}, [machines]);
|
}, [machines]);
|
||||||
|
const orderedMachines = useMemo(() => {
|
||||||
|
const seen = new Set(preferredMachineIds);
|
||||||
|
const preferred = preferredMachineIds.map((id) => machines.find((m) => m.id === id)).filter(Boolean) as { id: number; name: string }[];
|
||||||
|
const rest = machines.filter((m) => !seen.has(m.id));
|
||||||
|
return [...preferred, ...rest];
|
||||||
|
}, [machines]);
|
||||||
|
|
||||||
const pageSize = 20;
|
const pageSize = 20;
|
||||||
const totalPages = useMemo(() => (data ? Math.max(1, Math.ceil(data.total / data.pageSize)) : 1), [data]);
|
const totalPages = useMemo(() => (data ? Math.max(1, Math.ceil(data.total / data.pageSize)) : 1), [data]);
|
||||||
@@ -94,7 +109,7 @@ export default function ReleasesExplorer({
|
|||||||
if (year) params.set("year", year);
|
if (year) params.set("year", year);
|
||||||
if (sort) params.set("sort", sort);
|
if (sort) params.set("sort", sort);
|
||||||
if (dLanguageId) params.set("dLanguageId", dLanguageId);
|
if (dLanguageId) params.set("dLanguageId", dLanguageId);
|
||||||
if (dMachinetypeId) params.set("dMachinetypeId", dMachinetypeId);
|
if (dMachinetypeIds.length > 0) params.set("dMachinetypeId", dMachinetypeIds.join(","));
|
||||||
if (filetypeId) params.set("filetypeId", filetypeId);
|
if (filetypeId) params.set("filetypeId", filetypeId);
|
||||||
if (schemetypeId) params.set("schemetypeId", schemetypeId);
|
if (schemetypeId) params.set("schemetypeId", schemetypeId);
|
||||||
if (sourcetypeId) params.set("sourcetypeId", sourcetypeId);
|
if (sourcetypeId) params.set("sourcetypeId", sourcetypeId);
|
||||||
@@ -114,7 +129,7 @@ export default function ReleasesExplorer({
|
|||||||
if (year) params.set("year", String(Number(year)));
|
if (year) params.set("year", String(Number(year)));
|
||||||
if (sort) params.set("sort", sort);
|
if (sort) params.set("sort", sort);
|
||||||
if (dLanguageId) params.set("dLanguageId", dLanguageId);
|
if (dLanguageId) params.set("dLanguageId", dLanguageId);
|
||||||
if (dMachinetypeId) params.set("dMachinetypeId", dMachinetypeId);
|
if (dMachinetypeIds.length > 0) params.set("dMachinetypeId", dMachinetypeIds.join(","));
|
||||||
if (filetypeId) params.set("filetypeId", filetypeId);
|
if (filetypeId) params.set("filetypeId", filetypeId);
|
||||||
if (schemetypeId) params.set("schemetypeId", schemetypeId);
|
if (schemetypeId) params.set("schemetypeId", schemetypeId);
|
||||||
if (sourcetypeId) params.set("sourcetypeId", sourcetypeId);
|
if (sourcetypeId) params.set("sourcetypeId", sourcetypeId);
|
||||||
@@ -148,7 +163,7 @@ export default function ReleasesExplorer({
|
|||||||
(initialUrlState?.year ?? "") === (year ?? "") &&
|
(initialUrlState?.year ?? "") === (year ?? "") &&
|
||||||
sort === (initialUrlState?.sort ?? "year_desc") &&
|
sort === (initialUrlState?.sort ?? "year_desc") &&
|
||||||
(initialUrlState?.dLanguageId ?? "") === dLanguageId &&
|
(initialUrlState?.dLanguageId ?? "") === dLanguageId &&
|
||||||
(initialUrlState?.dMachinetypeId ?? "") === dMachinetypeId &&
|
parseMachineIds(initialUrlState?.dMachinetypeId).join(",") === dMachinetypeIds.join(",") &&
|
||||||
(initialUrlState?.filetypeId ?? "") === filetypeId &&
|
(initialUrlState?.filetypeId ?? "") === filetypeId &&
|
||||||
(initialUrlState?.schemetypeId ?? "") === schemetypeId &&
|
(initialUrlState?.schemetypeId ?? "") === schemetypeId &&
|
||||||
(initialUrlState?.sourcetypeId ?? "") === sourcetypeId &&
|
(initialUrlState?.sourcetypeId ?? "") === sourcetypeId &&
|
||||||
@@ -168,7 +183,7 @@ export default function ReleasesExplorer({
|
|||||||
}
|
}
|
||||||
updateUrl(page);
|
updateUrl(page);
|
||||||
fetchData(appliedQ, page);
|
fetchData(appliedQ, page);
|
||||||
}, [page, year, sort, dLanguageId, dMachinetypeId, filetypeId, schemetypeId, sourcetypeId, casetypeId, isDemo, appliedQ]);
|
}, [page, year, sort, dLanguageId, dMachinetypeIds, filetypeId, schemetypeId, sourcetypeId, casetypeId, isDemo, appliedQ]);
|
||||||
|
|
||||||
function onSubmit(e: React.FormEvent) {
|
function onSubmit(e: React.FormEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -209,14 +224,14 @@ export default function ReleasesExplorer({
|
|||||||
if (year) params.set("year", year);
|
if (year) params.set("year", year);
|
||||||
if (sort) params.set("sort", sort);
|
if (sort) params.set("sort", sort);
|
||||||
if (dLanguageId) params.set("dLanguageId", dLanguageId);
|
if (dLanguageId) params.set("dLanguageId", dLanguageId);
|
||||||
if (dMachinetypeId) params.set("dMachinetypeId", dMachinetypeId);
|
if (dMachinetypeIds.length > 0) params.set("dMachinetypeId", dMachinetypeIds.join(","));
|
||||||
if (filetypeId) params.set("filetypeId", filetypeId);
|
if (filetypeId) params.set("filetypeId", filetypeId);
|
||||||
if (schemetypeId) params.set("schemetypeId", schemetypeId);
|
if (schemetypeId) params.set("schemetypeId", schemetypeId);
|
||||||
if (sourcetypeId) params.set("sourcetypeId", sourcetypeId);
|
if (sourcetypeId) params.set("sourcetypeId", sourcetypeId);
|
||||||
if (casetypeId) params.set("casetypeId", casetypeId);
|
if (casetypeId) params.set("casetypeId", casetypeId);
|
||||||
if (isDemo) params.set("isDemo", "1");
|
if (isDemo) params.set("isDemo", "1");
|
||||||
return `/zxdb/releases?${params.toString()}`;
|
return `/zxdb/releases?${params.toString()}`;
|
||||||
}, [appliedQ, data?.page, year, sort, dLanguageId, dMachinetypeId, filetypeId, schemetypeId, sourcetypeId, casetypeId, isDemo]);
|
}, [appliedQ, data?.page, year, sort, dLanguageId, dMachinetypeIds, filetypeId, schemetypeId, sourcetypeId, casetypeId, isDemo]);
|
||||||
|
|
||||||
const nextHref = useMemo(() => {
|
const nextHref = useMemo(() => {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
@@ -225,14 +240,14 @@ export default function ReleasesExplorer({
|
|||||||
if (year) params.set("year", year);
|
if (year) params.set("year", year);
|
||||||
if (sort) params.set("sort", sort);
|
if (sort) params.set("sort", sort);
|
||||||
if (dLanguageId) params.set("dLanguageId", dLanguageId);
|
if (dLanguageId) params.set("dLanguageId", dLanguageId);
|
||||||
if (dMachinetypeId) params.set("dMachinetypeId", dMachinetypeId);
|
if (dMachinetypeIds.length > 0) params.set("dMachinetypeId", dMachinetypeIds.join(","));
|
||||||
if (filetypeId) params.set("filetypeId", filetypeId);
|
if (filetypeId) params.set("filetypeId", filetypeId);
|
||||||
if (schemetypeId) params.set("schemetypeId", schemetypeId);
|
if (schemetypeId) params.set("schemetypeId", schemetypeId);
|
||||||
if (sourcetypeId) params.set("sourcetypeId", sourcetypeId);
|
if (sourcetypeId) params.set("sourcetypeId", sourcetypeId);
|
||||||
if (casetypeId) params.set("casetypeId", casetypeId);
|
if (casetypeId) params.set("casetypeId", casetypeId);
|
||||||
if (isDemo) params.set("isDemo", "1");
|
if (isDemo) params.set("isDemo", "1");
|
||||||
return `/zxdb/releases?${params.toString()}`;
|
return `/zxdb/releases?${params.toString()}`;
|
||||||
}, [appliedQ, data?.page, year, sort, dLanguageId, dMachinetypeId, filetypeId, schemetypeId, sourcetypeId, casetypeId, isDemo]);
|
}, [appliedQ, data?.page, year, sort, dLanguageId, dMachinetypeIds, filetypeId, schemetypeId, sourcetypeId, casetypeId, isDemo]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -291,15 +306,34 @@ export default function ReleasesExplorer({
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="form-label small text-secondary">DL Machine</label>
|
<label className="form-label small text-secondary">DL Machine</label>
|
||||||
<select className="form-select" value={dMachinetypeId} onChange={(e) => { setDMachinetypeId(e.target.value); setPage(1); }}>
|
<div className="d-flex flex-wrap gap-2">
|
||||||
<option value="">All machines</option>
|
{orderedMachines.map((m) => {
|
||||||
{machines.map((m) => (
|
const active = dMachinetypeIds.includes(m.id);
|
||||||
<option key={m.id} value={m.id}>{m.name}</option>
|
return (
|
||||||
))}
|
<button
|
||||||
</select>
|
key={m.id}
|
||||||
{dMachinetypeId === "" && (
|
type="button"
|
||||||
<div className="form-text">Preferred: {preferredMachineNames.join(", ")}</div>
|
className={`btn btn-sm ${active ? "btn-primary" : "btn-outline-secondary"}`}
|
||||||
)}
|
onClick={() => {
|
||||||
|
setDMachinetypeIds((current) => {
|
||||||
|
const next = new Set(current);
|
||||||
|
if (next.has(m.id)) {
|
||||||
|
next.delete(m.id);
|
||||||
|
} else {
|
||||||
|
next.add(m.id);
|
||||||
|
}
|
||||||
|
const order = orderedMachines.map((item) => item.id);
|
||||||
|
return order.filter((id) => next.has(id));
|
||||||
|
});
|
||||||
|
setPage(1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{m.name}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className="form-text">Preferred: {preferredMachineNames.join(", ")}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="form-label small text-secondary">File type</label>
|
<label className="form-label small text-secondary">File type</label>
|
||||||
|
|||||||
@@ -7,6 +7,16 @@ export const metadata = {
|
|||||||
|
|
||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
|
function parseIdList(value: string | string[] | undefined) {
|
||||||
|
if (!value) return undefined;
|
||||||
|
const raw = Array.isArray(value) ? value.join(",") : value;
|
||||||
|
const ids = raw
|
||||||
|
.split(",")
|
||||||
|
.map((id) => Number(id.trim()))
|
||||||
|
.filter((id) => Number.isFinite(id) && id > 0);
|
||||||
|
return ids.length ? ids : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export default async function Page({ searchParams }: { searchParams: Promise<{ [key: string]: string | string[] | undefined }> }) {
|
export default async function Page({ searchParams }: { searchParams: Promise<{ [key: string]: string | string[] | undefined }> }) {
|
||||||
const sp = await searchParams;
|
const sp = await searchParams;
|
||||||
const hasParams = Object.values(sp).some((value) => value !== undefined);
|
const hasParams = Object.values(sp).some((value) => value !== undefined);
|
||||||
@@ -16,8 +26,9 @@ export default async function Page({ searchParams }: { searchParams: Promise<{ [
|
|||||||
const year = yearStr ? Number(yearStr) : undefined;
|
const year = yearStr ? Number(yearStr) : undefined;
|
||||||
const sort = ((Array.isArray(sp.sort) ? sp.sort[0] : sp.sort) ?? "year_desc") as "year_desc" | "year_asc" | "title" | "entry_id_desc";
|
const sort = ((Array.isArray(sp.sort) ? sp.sort[0] : sp.sort) ?? "year_desc") as "year_desc" | "year_asc" | "title" | "entry_id_desc";
|
||||||
const dLanguageId = (Array.isArray(sp.dLanguageId) ? sp.dLanguageId[0] : sp.dLanguageId) ?? "";
|
const dLanguageId = (Array.isArray(sp.dLanguageId) ? sp.dLanguageId[0] : sp.dLanguageId) ?? "";
|
||||||
const dMachinetypeIdStr = (Array.isArray(sp.dMachinetypeId) ? sp.dMachinetypeId[0] : sp.dMachinetypeId) ?? "";
|
const preferredMachineIds = [27, 26, 8, 9];
|
||||||
const dMachinetypeId = dMachinetypeIdStr ? Number(dMachinetypeIdStr) : undefined;
|
const dMachinetypeIds = parseIdList(sp.dMachinetypeId) ?? preferredMachineIds;
|
||||||
|
const dMachinetypeIdStr = dMachinetypeIds.join(",");
|
||||||
const filetypeIdStr = (Array.isArray(sp.filetypeId) ? sp.filetypeId[0] : sp.filetypeId) ?? "";
|
const filetypeIdStr = (Array.isArray(sp.filetypeId) ? sp.filetypeId[0] : sp.filetypeId) ?? "";
|
||||||
const filetypeId = filetypeIdStr ? Number(filetypeIdStr) : undefined;
|
const filetypeId = filetypeIdStr ? Number(filetypeIdStr) : undefined;
|
||||||
const schemetypeId = (Array.isArray(sp.schemetypeId) ? sp.schemetypeId[0] : sp.schemetypeId) ?? "";
|
const schemetypeId = (Array.isArray(sp.schemetypeId) ? sp.schemetypeId[0] : sp.schemetypeId) ?? "";
|
||||||
@@ -27,7 +38,7 @@ export default async function Page({ searchParams }: { searchParams: Promise<{ [
|
|||||||
const isDemo = isDemoStr ? (isDemoStr === "true" || isDemoStr === "1") : undefined;
|
const isDemo = isDemoStr ? (isDemoStr === "true" || isDemoStr === "1") : undefined;
|
||||||
|
|
||||||
const [initial, langs, machines, filetypes, schemes, sources, cases] = await Promise.all([
|
const [initial, langs, machines, filetypes, schemes, sources, cases] = await Promise.all([
|
||||||
searchReleases({ page, pageSize: 20, q, year, sort, dLanguageId: dLanguageId || undefined, dMachinetypeId, filetypeId, schemetypeId: schemetypeId || undefined, sourcetypeId: sourcetypeId || undefined, casetypeId: casetypeId || undefined, isDemo }),
|
searchReleases({ page, pageSize: 20, q, year, sort, dLanguageId: dLanguageId || undefined, dMachinetypeId: dMachinetypeIds, filetypeId, schemetypeId: schemetypeId || undefined, sourcetypeId: sourcetypeId || undefined, casetypeId: casetypeId || undefined, isDemo }),
|
||||||
listLanguages(),
|
listLanguages(),
|
||||||
listMachinetypes(),
|
listMachinetypes(),
|
||||||
listFiletypes(),
|
listFiletypes(),
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export interface SearchParams {
|
|||||||
// Optional simple filters (ANDed together)
|
// Optional simple filters (ANDed together)
|
||||||
genreId?: number;
|
genreId?: number;
|
||||||
languageId?: string;
|
languageId?: string;
|
||||||
machinetypeId?: number;
|
machinetypeId?: number | number[];
|
||||||
// Sorting
|
// Sorting
|
||||||
sort?: "title" | "id_desc";
|
sort?: "title" | "id_desc";
|
||||||
// Search scope (defaults to titles only)
|
// Search scope (defaults to titles only)
|
||||||
@@ -177,7 +177,10 @@ export async function searchEntries(params: SearchParams): Promise<PagedResult<S
|
|||||||
const offset = (page - 1) * pageSize;
|
const offset = (page - 1) * pageSize;
|
||||||
const sort = params.sort ?? (q ? "title" : "id_desc");
|
const sort = params.sort ?? (q ? "title" : "id_desc");
|
||||||
const scope: EntrySearchScope = params.scope ?? "title";
|
const scope: EntrySearchScope = params.scope ?? "title";
|
||||||
const preferMachineOrder = typeof params.machinetypeId === "number"
|
const hasMachineFilter = Array.isArray(params.machinetypeId)
|
||||||
|
? params.machinetypeId.length > 0
|
||||||
|
: typeof params.machinetypeId === "number";
|
||||||
|
const preferMachineOrder = hasMachineFilter
|
||||||
? null
|
? null
|
||||||
: sql`case
|
: sql`case
|
||||||
when ${entries.machinetypeId} = 27 then 0
|
when ${entries.machinetypeId} = 27 then 0
|
||||||
@@ -190,7 +193,7 @@ export async function searchEntries(params: SearchParams): Promise<PagedResult<S
|
|||||||
if (q.length === 0) {
|
if (q.length === 0) {
|
||||||
// Default listing: return first page by id desc (no guaranteed ordering field; using id)
|
// Default listing: return first page by id desc (no guaranteed ordering field; using id)
|
||||||
// Apply optional filters even without q
|
// Apply optional filters even without q
|
||||||
const whereClauses: Array<ReturnType<typeof eq>> = [];
|
const whereClauses: Array<ReturnType<typeof sql>> = [];
|
||||||
if (typeof params.genreId === "number") {
|
if (typeof params.genreId === "number") {
|
||||||
whereClauses.push(eq(entries.genretypeId, params.genreId));
|
whereClauses.push(eq(entries.genretypeId, params.genreId));
|
||||||
}
|
}
|
||||||
@@ -199,6 +202,9 @@ export async function searchEntries(params: SearchParams): Promise<PagedResult<S
|
|||||||
}
|
}
|
||||||
if (typeof params.machinetypeId === "number") {
|
if (typeof params.machinetypeId === "number") {
|
||||||
whereClauses.push(eq(entries.machinetypeId, params.machinetypeId));
|
whereClauses.push(eq(entries.machinetypeId, params.machinetypeId));
|
||||||
|
} else if (Array.isArray(params.machinetypeId) && params.machinetypeId.length > 0) {
|
||||||
|
const ids = params.machinetypeId.map((id) => sql`${id}`);
|
||||||
|
whereClauses.push(sql`${entries.machinetypeId} in (${sql.join(ids, sql`, `)})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const whereExpr = whereClauses.length ? and(...whereClauses) : undefined;
|
const whereExpr = whereClauses.length ? and(...whereClauses) : undefined;
|
||||||
@@ -1655,7 +1661,12 @@ export async function getEntryFacets(params: SearchParams): Promise<EntryFacets>
|
|||||||
}
|
}
|
||||||
if (params.genreId) whereParts.push(sql`e.genretype_id = ${params.genreId}`);
|
if (params.genreId) whereParts.push(sql`e.genretype_id = ${params.genreId}`);
|
||||||
if (params.languageId) whereParts.push(sql`e.language_id = ${params.languageId}`);
|
if (params.languageId) whereParts.push(sql`e.language_id = ${params.languageId}`);
|
||||||
if (params.machinetypeId) whereParts.push(sql`e.machinetype_id = ${params.machinetypeId}`);
|
if (typeof params.machinetypeId === "number") {
|
||||||
|
whereParts.push(sql`e.machinetype_id = ${params.machinetypeId}`);
|
||||||
|
} else if (Array.isArray(params.machinetypeId) && params.machinetypeId.length > 0) {
|
||||||
|
const ids = params.machinetypeId.map((id) => sql`${id}`);
|
||||||
|
whereParts.push(sql`e.machinetype_id in (${sql.join(ids, sql`, `)})`);
|
||||||
|
}
|
||||||
|
|
||||||
const whereSql = whereParts.length ? sql.join([sql`where `, sql.join(whereParts, sql` and `)], sql``) : sql``;
|
const whereSql = whereParts.length ? sql.join([sql`where `, sql.join(whereParts, sql` and `)], sql``) : sql``;
|
||||||
|
|
||||||
@@ -1733,7 +1744,7 @@ export interface ReleaseSearchParams {
|
|||||||
sort?: "year_desc" | "year_asc" | "title" | "entry_id_desc";
|
sort?: "year_desc" | "year_asc" | "title" | "entry_id_desc";
|
||||||
// Optional download-based filters (matched via EXISTS on downloads)
|
// Optional download-based filters (matched via EXISTS on downloads)
|
||||||
dLanguageId?: string; // downloads.language_id
|
dLanguageId?: string; // downloads.language_id
|
||||||
dMachinetypeId?: number; // downloads.machinetype_id
|
dMachinetypeId?: number | number[]; // downloads.machinetype_id
|
||||||
filetypeId?: number; // downloads.filetype_id
|
filetypeId?: number; // downloads.filetype_id
|
||||||
schemetypeId?: string; // downloads.schemetype_id
|
schemetypeId?: string; // downloads.schemetype_id
|
||||||
sourcetypeId?: string; // downloads.sourcetype_id
|
sourcetypeId?: string; // downloads.sourcetype_id
|
||||||
@@ -1754,7 +1765,10 @@ export async function searchReleases(params: ReleaseSearchParams): Promise<Paged
|
|||||||
const pageSize = Math.max(1, Math.min(params.pageSize ?? 20, 100));
|
const pageSize = Math.max(1, Math.min(params.pageSize ?? 20, 100));
|
||||||
const page = Math.max(1, params.page ?? 1);
|
const page = Math.max(1, params.page ?? 1);
|
||||||
const offset = (page - 1) * pageSize;
|
const offset = (page - 1) * pageSize;
|
||||||
const preferMachineOrder = params.dMachinetypeId != null
|
const hasMachineFilter = Array.isArray(params.dMachinetypeId)
|
||||||
|
? params.dMachinetypeId.length > 0
|
||||||
|
: params.dMachinetypeId != null;
|
||||||
|
const preferMachineOrder = hasMachineFilter
|
||||||
? null
|
? null
|
||||||
: sql`case
|
: sql`case
|
||||||
when ${entries.machinetypeId} = 27 then 0
|
when ${entries.machinetypeId} = 27 then 0
|
||||||
@@ -1780,7 +1794,12 @@ export async function searchReleases(params: ReleaseSearchParams): Promise<Paged
|
|||||||
// would produce "from `d`" which MySQL interprets as a literal table.
|
// would produce "from `d`" which MySQL interprets as a literal table.
|
||||||
const dlConds: Array<ReturnType<typeof sql>> = [];
|
const dlConds: Array<ReturnType<typeof sql>> = [];
|
||||||
if (params.dLanguageId) dlConds.push(sql`d.language_id = ${params.dLanguageId}`);
|
if (params.dLanguageId) dlConds.push(sql`d.language_id = ${params.dLanguageId}`);
|
||||||
if (params.dMachinetypeId != null) dlConds.push(sql`d.machinetype_id = ${params.dMachinetypeId}`);
|
if (typeof params.dMachinetypeId === "number") {
|
||||||
|
dlConds.push(sql`d.machinetype_id = ${params.dMachinetypeId}`);
|
||||||
|
} else if (Array.isArray(params.dMachinetypeId) && params.dMachinetypeId.length > 0) {
|
||||||
|
const ids = params.dMachinetypeId.map((id) => sql`${id}`);
|
||||||
|
dlConds.push(sql`d.machinetype_id in (${sql.join(ids, sql`, `)})`);
|
||||||
|
}
|
||||||
if (params.filetypeId != null) dlConds.push(sql`d.filetype_id = ${params.filetypeId}`);
|
if (params.filetypeId != null) dlConds.push(sql`d.filetype_id = ${params.filetypeId}`);
|
||||||
if (params.schemetypeId) dlConds.push(sql`d.schemetype_id = ${params.schemetypeId}`);
|
if (params.schemetypeId) dlConds.push(sql`d.schemetype_id = ${params.schemetypeId}`);
|
||||||
if (params.sourcetypeId) dlConds.push(sql`d.sourcetype_id = ${params.sourcetypeId}`);
|
if (params.sourcetypeId) dlConds.push(sql`d.sourcetype_id = ${params.sourcetypeId}`);
|
||||||
|
|||||||
Reference in New Issue
Block a user