import { searchPath } from "@/lib/frontend/routes"; import { serializeSearchUrlState, SEARCH_SCOPE, } from "@/lib/frontend/search/urlState"; import { isValidBranchParam } from "@/lib/frontend/params"; /** * Build the full /:branch/search href for a given state. * * @param {{ routeBranch: string, state: any }} input * @returns {string} */ export function buildSearchHref({ routeBranch, state }) { const base = searchPath(routeBranch); const qs = serializeSearchUrlState(state); return qs ? `${base}?${qs}` : base; } /** * Build a stable searchKey identity (cursor excluded). * Includes routeBranch to avoid cross-branch race conditions. * * @param {{ routeBranch: string, urlState: any }} input * @returns {string} */ export function buildSearchKey({ routeBranch, urlState }) { const qs = serializeSearchUrlState(urlState); return `${routeBranch}|${qs}`; } /** * Derive the results description label for the current scope. * * @param {{ routeBranch: string, urlState: any }} input * @returns {string} */ export function getScopeLabel({ routeBranch, urlState }) { const multiCount = Array.isArray(urlState?.branches) ? urlState.branches.length : 0; if (urlState?.scope === SEARCH_SCOPE.ALL) return "Alle Niederlassungen"; if (urlState?.scope === SEARCH_SCOPE.MULTI) { if (multiCount > 0) { return `${multiCount} Niederlassung${multiCount === 1 ? "" : "en"}`; } return "Mehrere Niederlassungen"; } return `Niederlassung ${routeBranch}`; } /** * Whether the UI should show the "select at least one branch" hint. * * @param {{ isAdminDev: boolean, urlState: any }} input * @returns {boolean} */ export function needsMultiBranchSelectionHint({ isAdminDev, urlState }) { const multiCount = Array.isArray(urlState?.branches) ? urlState.branches.length : 0; return ( Boolean(isAdminDev) && urlState?.scope === SEARCH_SCOPE.MULTI && Boolean(urlState?.q) && multiCount === 0 ); } /** * Build the next state for scope changes. * * @param {{ urlState: any, nextScope: string }} input * @returns {any} */ export function buildNextStateForScopeChange({ urlState, nextScope }) { return { ...urlState, scope: nextScope, branches: nextScope === SEARCH_SCOPE.MULTI ? urlState.branches : [], }; } /** * Toggle one branch in MULTI mode. * * @param {{ urlState: any, branchId: string }} input * @returns {any} */ export function buildNextStateForToggleBranch({ urlState, branchId }) { const current = Array.isArray(urlState?.branches) ? urlState.branches : []; const set = new Set(current); if (set.has(branchId)) set.delete(branchId); else set.add(branchId); return { ...urlState, scope: SEARCH_SCOPE.MULTI, branches: Array.from(set), }; } /** * Clear all branches in MULTI mode. * * @param {{ urlState: any }} input * @returns {any} */ export function buildNextStateForClearAllBranches({ urlState }) { return { ...urlState, scope: SEARCH_SCOPE.MULTI, branches: [], }; } /** * Build a navigation target for switching SINGLE branch (path segment). * * @param {{ nextBranch: string, urlState: any }} input * @returns {string|null} */ export function buildHrefForSingleBranchSwitch({ nextBranch, urlState }) { if (!isValidBranchParam(nextBranch)) return null; const nextState = { ...urlState, scope: SEARCH_SCOPE.SINGLE, branches: [], }; return buildSearchHref({ routeBranch: nextBranch, state: nextState }); }