| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687 |
- 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 };
- }
|