From f851305d7881a1d72f1f012198790c555a7f1437 Mon Sep 17 00:00:00 2001 From: opencode Date: Mon, 3 Aug 2026 08:48:08 +0200 Subject: [PATCH] fix(tools): OR'ed where clauses now AND'd so search excludes soft-deleted tools; localize delete confirm dialog --- artifacts/api-server/src/routes/tools.ts | 14 ++++++++------ artifacts/toolrate/src/i18n/locales/de.json | 7 ++++++- artifacts/toolrate/src/i18n/locales/en.json | 7 ++++++- artifacts/toolrate/src/pages/tool-detail.tsx | 10 +++++----- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/artifacts/api-server/src/routes/tools.ts b/artifacts/api-server/src/routes/tools.ts index 18fad7c..3758811 100644 --- a/artifacts/api-server/src/routes/tools.ts +++ b/artifacts/api-server/src/routes/tools.ts @@ -1,5 +1,5 @@ import { Router, type IRouter } from "express"; -import { eq, desc, asc, sql, and, not, isNull, inArray } from "drizzle-orm"; +import { eq, desc, asc, sql, and, not, isNull, inArray, type SQL } from "drizzle-orm"; import { z } from "zod"; import { db, toolsTable, ratingsTable, toolRelationsTable } from "@workspace/db"; import { @@ -57,21 +57,23 @@ router.get("/tools", async (req, res): Promise => { const tagList = (tags ?? "").split(",").map((t) => t.trim()).filter(Boolean); const featureList = (features ?? "").split(",").map((f) => f.trim()).filter(Boolean); - let query = db.select().from(toolsTable).where(isNull(toolsTable.deletedAt)).$dynamic(); + const conditions: SQL[] = [isNull(toolsTable.deletedAt)]; if (category) { - query = query.where(eq(toolsTable.category, category)); + conditions.push(eq(toolsTable.category, category)); } if (search) { const escaped = search.replace(/[%_\\]/g, (m) => `\\${m}`); - query = query.where(sql`${toolsTable.name} ilike ${`%${escaped}%`} escape '\\'`); + conditions.push(sql`${toolsTable.name} ilike ${`%${escaped}%`} escape '\\'`); } if (tagList.length > 0) { - query = query.where(sql`${toolsTable.tags} @> ARRAY[${sql.join(tagList.map((t) => sql`${t}`), sql`, `)}]::text[]`); + conditions.push(sql`${toolsTable.tags} @> ARRAY[${sql.join(tagList.map((t) => sql`${t}`), sql`, `)}]::text[]`); } if (featureList.length > 0) { - query = query.where(sql`${toolsTable.features} @> ARRAY[${sql.join(featureList.map((f) => sql`${f}`), sql`, `)}]::text[]`); + conditions.push(sql`${toolsTable.features} @> ARRAY[${sql.join(featureList.map((f) => sql`${f}`), sql`, `)}]::text[]`); } + const query = db.select().from(toolsTable).where(and(...conditions)); + const tools = await query.orderBy(desc(toolsTable.createdAt)); const toolIds = tools.map((t) => t.id); diff --git a/artifacts/toolrate/src/i18n/locales/de.json b/artifacts/toolrate/src/i18n/locales/de.json index 5c0927b..b69f8e7 100644 --- a/artifacts/toolrate/src/i18n/locales/de.json +++ b/artifacts/toolrate/src/i18n/locales/de.json @@ -127,7 +127,12 @@ "relatedTools": "Ähnliche Tools", "costs": "Kosten", "addCost": "Kosten hinzufügen", - "recentRatings": "Letzte Bewertungen" + "recentRatings": "Letzte Bewertungen", + "deleteConfirmTitle": "Dieses Tool löschen?", + "deleteToTrash": "Dies verschiebt {{name}} in den Papierkorb. Es kann später wiederhergestellt werden.", + "deletePermanent": "Dies entfernt {{name}} dauerhaft inklusive aller Bewertungen. Das kann nicht rückgängig gemacht werden.", + "deleting": "Löschen…", + "deleteAction": "Löschen" }, "compare": { "title": "Tools vergleichen", diff --git a/artifacts/toolrate/src/i18n/locales/en.json b/artifacts/toolrate/src/i18n/locales/en.json index 68192da..9e04bcc 100644 --- a/artifacts/toolrate/src/i18n/locales/en.json +++ b/artifacts/toolrate/src/i18n/locales/en.json @@ -127,7 +127,12 @@ "relatedTools": "Related Tools", "costs": "Costs", "addCost": "Add Cost", - "recentRatings": "Recent Ratings" + "recentRatings": "Recent Ratings", + "deleteConfirmTitle": "Delete this tool?", + "deleteToTrash": "This will move {{name}} to the trash. It can be restored later.", + "deletePermanent": "This will permanently remove {{name}} and all its ratings. This cannot be undone.", + "deleting": "Deleting…", + "deleteAction": "Delete" }, "compare": { "title": "Compare Tools", diff --git a/artifacts/toolrate/src/pages/tool-detail.tsx b/artifacts/toolrate/src/pages/tool-detail.tsx index edc1b69..ada05eb 100644 --- a/artifacts/toolrate/src/pages/tool-detail.tsx +++ b/artifacts/toolrate/src/pages/tool-detail.tsx @@ -914,23 +914,23 @@ export default function ToolDetail() { - Delete this tool? + {t("detail.deleteConfirmTitle")} {hasTrash ? ( - <>This will move {tool?.name} to the trash. It can be restored later. + <>{t("detail.deleteToTrash", { name: tool?.name })} ) : ( - <>This will permanently remove {tool?.name} and all its ratings. This cannot be undone. + <>{t("detail.deletePermanent", { name: tool?.name })} )} - Cancel + {t("common.cancel")} - {deleteTool.isPending ? "Deleting…" : "Delete"} + {deleteTool.isPending ? t("detail.deleting") : t("detail.deleteAction")}