import { SEARCH_SCOPE } from "@/lib/frontend/search/urlState"; import { buildDateFilterValidationError } from "@/lib/frontend/search/dateFilterValidation"; import { normalizeIsoDateYmdOrNull } from "@/lib/frontend/search/dateRange"; function isNonEmptyString(value) { return typeof value === "string" && value.trim().length > 0; } /** * Build the apiClient.search(...) input from URL state + current user context. * * Return shape: * - input: object for apiClient.search(...) or null (no search yet / not ready) * - error: ApiClientError or null (local validation / fast-fail) * * UX policy for MULTI without branches: * - Treat it as "not ready" (input=null, error=null) instead of an error. */ export function buildSearchApiInput({ urlState, routeBranch, user, cursor = null, limit = 100, }) { const q = isNonEmptyString(urlState?.q) ? urlState.q.trim() : null; // UI policy (RHL-024): q is required to trigger a search. if (!q) return { input: null, error: null }; // DRY: validate date filters via shared pure helper (RHL-025 UX + consistency). const dateErr = buildDateFilterValidationError({ from: urlState?.from ?? null, to: urlState?.to ?? null, }); if (dateErr) return { input: null, error: dateErr }; const input = { q, limit }; // Normalize (trim + validity) before passing to apiClient. const from = normalizeIsoDateYmdOrNull(urlState?.from); const to = normalizeIsoDateYmdOrNull(urlState?.to); if (from) input.from = from; if (to) input.to = to; if (isNonEmptyString(cursor)) input.cursor = cursor.trim(); const role = user?.role; // Branch users: always restricted to the current route branch. if (role === "branch") { input.branch = routeBranch; return { input, error: null }; } // Admin/dev: respect scope rules from URL state. if (role === "admin" || role === "dev") { if (urlState.scope === SEARCH_SCOPE.ALL) { input.scope = "all"; return { input, error: null }; } if (urlState.scope === SEARCH_SCOPE.MULTI) { const branches = Array.isArray(urlState.branches) ? urlState.branches : []; // UX: missing branches is not an error, it's "not ready". if (branches.length === 0) { return { input: null, error: null }; } input.scope = "multi"; input.branches = branches; return { input, error: null }; } // SINGLE input.branch = routeBranch; return { input, error: null }; } // Unknown role: fail-safe to single-branch using the route context. input.branch = routeBranch; return { input, error: null }; }