feat(reports): parseOperationalFilters pure parser (Area scope)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
27
src/lib/services/reports/operational-filters.ts
Normal file
27
src/lib/services/reports/operational-filters.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Operational report filters. Mirrors `sales-filters.ts`: the parser is a
|
||||
* pure, unit-testable function so the route just hands it the query params.
|
||||
*
|
||||
* Beta scope is Area only (a berth-area scope). The shape is intentionally
|
||||
* an object so a Status dimension can be added later without a rename.
|
||||
*/
|
||||
export interface OperationalFilters {
|
||||
areas?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the `area` CSV query param into a free list of port-defined area
|
||||
* strings. Empty / whitespace entries are dropped. Drizzle parameterises
|
||||
* the downstream `inArray`, so unvalidated values are injection-safe.
|
||||
* Returns `undefined` when no areas are active (→ no filter).
|
||||
*/
|
||||
export function parseOperationalFilters(params: URLSearchParams): OperationalFilters | undefined {
|
||||
const raw = params.get('area');
|
||||
if (!raw) return undefined;
|
||||
const areas = raw
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter((s) => s.length > 0);
|
||||
if (areas.length === 0) return undefined;
|
||||
return { areas };
|
||||
}
|
||||
29
tests/unit/services/reports/operational-filters.test.ts
Normal file
29
tests/unit/services/reports/operational-filters.test.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { parseOperationalFilters } from '@/lib/services/reports/operational-filters';
|
||||
|
||||
function params(qs: string): URLSearchParams {
|
||||
return new URLSearchParams(qs);
|
||||
}
|
||||
|
||||
describe('parseOperationalFilters', () => {
|
||||
it('returns undefined when no area param is present', () => {
|
||||
expect(parseOperationalFilters(params(''))).toBeUndefined();
|
||||
expect(parseOperationalFilters(params('from=x&to=y'))).toBeUndefined();
|
||||
});
|
||||
|
||||
it('parses a single area', () => {
|
||||
expect(parseOperationalFilters(params('area=A'))).toEqual({ areas: ['A'] });
|
||||
});
|
||||
|
||||
it('parses a CSV of areas and trims whitespace', () => {
|
||||
expect(parseOperationalFilters(params('area=A,%20B%20,C'))).toEqual({
|
||||
areas: ['A', 'B', 'C'],
|
||||
});
|
||||
});
|
||||
|
||||
it('drops empty / whitespace-only entries, returning undefined when nothing is left', () => {
|
||||
expect(parseOperationalFilters(params('area=%20,%20'))).toBeUndefined();
|
||||
expect(parseOperationalFilters(params('area='))).toBeUndefined();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user