feat(11-03): CPV-Filter + Wert-min/max-UI in Builder, DTO, FilterPanel
TenderQueryDto gains a validated cpv[] field (single-or-repeated query
param, normalized via @Transform); buildTenderWhere adds a cpvDivisions
hasSome branch (FILTER-03, Pitfall 2 — never an exact match against raw
cpvCodes). FilterPanel gets a CPV-Division autocomplete (search-by-label,
multi-select chips, repeated ?cpv= params) plus the previously
backend-only value filter's UI: valueMin/valueMax number inputs and an
"ohne Wertangabe einschließen" toggle (default on, matches the builder's
includeNullValue default from Plan 11-01) so the 91.6% NULL-value rows
stay visible by default. Verified against the live DB: cpv=45 matches all
three raw formats ("45", "45000000", "45000000-7") via the backfilled
cpvDivisions column.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -90,6 +90,14 @@ export function buildTenderWhere(dto: TenderQueryDto): Prisma.TenderWhereInput {
|
||||
AND.push({ region: { startsWith: dto.region } });
|
||||
}
|
||||
|
||||
// FILTER-03 / D-03 (Pitfall 2): CPV-Division-Filter — matched via
|
||||
// `hasSome` against the normalizer/backfill-derived `cpvDivisions`
|
||||
// column (typesafe, GIN-indexable), never an exact-equality match
|
||||
// against the inconsistently-formatted raw `cpvCodes` array.
|
||||
if (dto.cpv?.length) {
|
||||
AND.push({ cpvDivisions: { hasSome: dto.cpv } });
|
||||
}
|
||||
|
||||
if (AND.length) where.AND = AND;
|
||||
return where;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user