All files / src/utils urlState.ts

100% Statements 82/82
100% Branches 24/24
100% Functions 3/3
100% Lines 82/82

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 831x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 14x 14x 6x 6x 14x 14x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 18x 18x 18x 18x 38x 38x 38x 38x 9x 9x 38x 38x 38x 38x 18x 18x 18x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 10x 10x 10x 15x 15x 10x 10x 10x 10x 10x 10x  
/**
 * @file urlState.ts
 * @description
 * Tiny helpers to read/write query params for the analytics filters.
 *
 * @enterprise
 * - Centralizes URL parsing/serialization so pages/components stay dumb.
 * - Backward-compatible: accepts both `supplierId` (canonical) and `supplierid`.
 * - Defensive: strips accidental surrounding quotes (e.g., `%22abc%22` → `abc`).
 */
 
/** Plain dict of URL params (undefined = absent) */
export type UrlDict = Record<string, string | undefined>;
 
/**
 * Strips surrounding double quotes if present.
 * Examples:
 *  - `"abc"` → `abc`
 *  - `abc`   → `abc`
 *  - `""`    → `undefined`
 * @internal
 */
function dequote(v: string | null): string | undefined {
  if (v == null) return undefined;
  const trimmed = v.trim();
  const unquoted = trimmed.replace(/^"+|"+$/g, '');
  return unquoted.length ? unquoted : undefined;
}
 
/**
 * Reads a subset of keys from the given search string into a plain object.
 *
 * @param search - The raw `location.search` string (may start with `?`).
 * @param keys   - Param names to read (e.g., `['from','to','supplierId']`).
 * @returns      - A dict with the requested keys (missing → `undefined`).
 *
 * @remarks
 * - Special handling for `supplierId`: if absent, we also look for legacy
 *   lowercase `supplierid` and we strip accidental surrounding quotes.
 */
export function readParams(search: string, keys: string[]): UrlDict {
  const sp = new URLSearchParams(search);
  const out: UrlDict = {};
 
  for (const k of keys) {
    let raw = sp.get(k);
 
    // Backward compatibility + robustness for supplierId
    if ((raw == null || raw === '') && k === 'supplierId') {
      raw = sp.get('supplierid');
    }
 
    // Strip accidental quotes for IDs (especially supplierId)
    out[k] = k === 'supplierId' ? dequote(raw) : (raw ?? undefined);
  }
 
  return out;
}
 
/**
 * Serializes a patch of params back into a search string.
 *
 * @param baseSearch - The existing `location.search` (preserved unless overwritten).
 * @param patch      - Key/value pairs to upsert (undefined/empty → removed).
 * @returns          - A `?key=value&...` string, or empty string when no params.
 *
 * @example
 * writeParams('?from=2025-01-01', { supplierId: 'abc', to: '2025-02-01' })
 * // → '?from=2025-01-01&supplierId=abc&to=2025-02-01'
 */
export function writeParams(baseSearch: string, patch: UrlDict): string {
  const sp = new URLSearchParams(baseSearch);
  // Apply the patch
  Object.entries(patch).forEach(([k, v]) => {
    const val = v?.trim?.() ?? v;
    if (val === undefined || val === null || val === '') sp.delete(k);
    else sp.set(k, val);
  });
  // Serialize back
  const s = sp.toString();
  return s ? `?${s}` : '';
}