فهرست منبع

RHL-024 refactor(search): add search function for delivery notes with query string construction and API integration

Code_Uwe 4 هفته پیش
والد
کامیت
fdf9d61f35
2فایلهای تغییر یافته به همراه104 افزوده شده و 0 حذف شده
  1. 54 0
      lib/frontend/apiClient.js
  2. 50 0
      lib/frontend/apiClient.test.js

+ 54 - 0
lib/frontend/apiClient.js

@@ -346,3 +346,57 @@ export function getFiles(branch, year, month, day, options) {
 
 	return apiFetch(`/api/files?${qs.toString()}`, { method: "GET", ...options });
 }
+
+/**
+ * Search delivery notes (RHL-024).
+ *
+ * Notes:
+ * - This endpoint is JSON and can be called via apiFetch.
+ * - Cursor is intentionally not stored in shareable URLs by default; the UI can keep it in state.
+ *
+ * @param {{
+ *   q?: string|null,
+ *   branch?: string|null,
+ *   scope?: "branch"|"multi"|"all"|string|null,
+ *   branches?: string[]|null,
+ *   from?: string|null,
+ *   to?: string|null,
+ *   limit?: number|null,
+ *   cursor?: string|null
+ * }} input
+ * @param {{ baseUrl?: string, fetchImpl?: typeof fetch }=} options
+ */
+export function search(input, options) {
+	const { q, branch, scope, branches, from, to, limit, cursor } = input || {};
+
+	const params = new URLSearchParams();
+
+	// Stable insertion order (helps debugging and makes URLs readable).
+	if (typeof q === "string" && q.trim()) params.set("q", q.trim());
+	if (typeof scope === "string" && scope.trim())
+		params.set("scope", scope.trim());
+	if (typeof branch === "string" && branch.trim())
+		params.set("branch", branch.trim());
+
+	if (Array.isArray(branches) && branches.length > 0) {
+		const cleaned = branches.map((b) => String(b).trim()).filter(Boolean);
+		if (cleaned.length > 0) params.set("branches", cleaned.join(","));
+	}
+
+	if (typeof from === "string" && from.trim()) params.set("from", from.trim());
+	if (typeof to === "string" && to.trim()) params.set("to", to.trim());
+
+	if (limit !== undefined && limit !== null) {
+		const raw = String(limit).trim();
+		if (raw) params.set("limit", raw);
+	}
+
+	if (typeof cursor === "string" && cursor.trim()) {
+		params.set("cursor", cursor.trim());
+	}
+
+	const qs = params.toString();
+	const path = qs ? `/api/search?${qs}` : "/api/search";
+
+	return apiFetch(path, { method: "GET", ...options });
+}

+ 50 - 0
lib/frontend/apiClient.test.js

@@ -7,6 +7,7 @@ import {
 	getFiles,
 	login,
 	getMe,
+	search,
 } from "./apiClient.js";
 
 beforeEach(() => {
@@ -139,4 +140,53 @@ describe("lib/frontend/apiClient", () => {
 		expect(init.credentials).toBe("include");
 		expect(init.cache).toBe("no-store");
 	});
+
+	it("search builds the expected query string for branch scope", async () => {
+		fetch.mockResolvedValue(
+			new Response(JSON.stringify({ items: [], nextCursor: null }), {
+				status: 200,
+				headers: { "Content-Type": "application/json" },
+			})
+		);
+
+		await search({ q: "bridgestone", branch: "NL01", limit: 100 });
+
+		expect(fetch).toHaveBeenCalledTimes(1);
+		const [url, init] = fetch.mock.calls[0];
+
+		const u = new URL(url, "http://localhost");
+		expect(u.pathname).toBe("/api/search");
+		expect(u.searchParams.get("q")).toBe("bridgestone");
+		expect(u.searchParams.get("branch")).toBe("NL01");
+		expect(u.searchParams.get("limit")).toBe("100");
+
+		expect(init.method).toBe("GET");
+		expect(init.credentials).toBe("include");
+		expect(init.cache).toBe("no-store");
+	});
+
+	it("search supports multi scope + branches + cursor", async () => {
+		fetch.mockResolvedValue(
+			new Response(JSON.stringify({ items: [], nextCursor: null }), {
+				status: 200,
+				headers: { "Content-Type": "application/json" },
+			})
+		);
+
+		await search({
+			q: "  reifen  ",
+			scope: "multi",
+			branches: ["NL06", "NL20"],
+			cursor: "abc",
+		});
+
+		const [url] = fetch.mock.calls[0];
+		const u = new URL(url, "http://localhost");
+
+		expect(u.pathname).toBe("/api/search");
+		expect(u.searchParams.get("q")).toBe("reifen");
+		expect(u.searchParams.get("scope")).toBe("multi");
+		expect(u.searchParams.get("branches")).toBe("NL06,NL20");
+		expect(u.searchParams.get("cursor")).toBe("abc");
+	});
 });