Skip to content

feat: add support for custom field filters in task listing#286

Merged
pikann merged 2 commits into
masterfrom
feature/add-support-for-custom-field-filter
Jul 17, 2026
Merged

feat: add support for custom field filters in task listing#286
pikann merged 2 commits into
masterfrom
feature/add-support-for-custom-field-filter

Conversation

@pikann

@pikann pikann commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds filtering by custom fields and several built-in task fields to view settings, available across every view type (Table/Board/Roadmap/Plugin — they share one settings panel and one filter-resolution path). Also fixes a filtering crash bug and removes two non-functional UI stubs.

What's new

Filtering (apps/web, services/api)

  • Custom fields — filter by any custom field, for all 7 field types: select/multi_select/boolean get a checklist selector, number/date get an inclusive min/max or after/before range, text/url get a debounced substring search. Sent as a single JSON query param (custom_field_filters) since fields are dynamic per project.
  • Built-in fields — start date, due date, story points (inclusive ranges), importance (multi-select of the 5 priority levels, resolved client-side into OR'd numeric ranges so non-contiguous picks like "Low or Critical" work correctly instead of silently spanning the skipped buckets), and tags (free-text chip input, ANY-of match).
  • New query params on GET /projects/:id/tasks: start_date_after/start_date_before, due_date_after/due_date_before, story_points_min/story_points_max, importance_ranges (JSON array of {min,max}, OR'd together), tags (comma-separated).
  • i18n: new filter labels translated across all 9 supported locales.

Bug fix: custom-field filter crash (SQLSTATE 22007)

Reported repro: create a select field, set a task's value to a string, delete the field, then create a new field reusing the same field_key but a date type — filtering on it crashed the whole query instead of just excluding the bad row.

  • DeleteCustomFieldDefinition now purges the field's stray values from every task in the project in the same transaction, so a later field reusing the key can no longer inherit incompatible data.
  • Filter/sort/sum SQL now casts custom_fields values through a regex-guarded CASE WHEN instead of a bare ::numeric/::date cast, so any remaining invalid value is excluded (treated as no-match) rather than aborting the query — this closes the same latent crash class in the pre-existing custom-field sort and field-sum code paths too, not just the new filter code.

Removed

  • "Track Time" and "Relationships" fields from the task properties panel — both were dead UI stubs with no backend wiring (no onClick, no data). Time tracking should go through a plugin; Linked Tasks already covers relationships.

Minor

  • Removed an unused test helper (newAttachmentRouter) caught by lint while working in that area.

Test plan

  • go build ./... && go vet ./... && golangci-lint run ./... — clean.
  • go test ./internal/... ./test/integration/... — full suite passes.
  • New e2e coverage: services/api/test/e2e/task_filter_test.go — 15 test functions / 35 subtests against a real Postgres, covering all 7 custom-field types, all 5 built-in filters (including the non-contiguous importance-range OR case), multi-dimension AND composition, total_count/field_sum respecting active filters, and validation-error paths (malformed dates, non-numeric story points, min > max). Ran PACA_E2E=1 go test ./test/e2e/... for the full suite — all green, no regressions.
  • apps/web: tsc -b, biome check ., and the full vitest suite all pass.

- Implemented parsing and handling of custom field filters in the task handler.
- Added tests for various custom field filter scenarios including select, number range, and date range filters.
- Enhanced error handling for invalid JSON and unknown field keys in custom field filters.
- Introduced regression tests to ensure stability against invalid stored values and field key reuse scenarios.
- Updated API documentation to reflect new query parameters for custom field filters and importance ranges.
@pullfrog

pullfrog Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Run failed. View the logs →

Pullfrog  | Rerun failed job ➔View workflow run | via Pullfrog | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@pikann pikann linked an issue Jul 17, 2026 that may be closed by this pull request
@pullfrog

pullfrog Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Important

Two concrete issues need fixing before merge: the date cast guard in the repository is missing an end anchor, and custom-field date bounds are not validated in the handler.

Reviewed changes — PR #286 adds custom field filtering for task listing (select, multi_select, boolean, number, date, text, url), plus built-in filters for start/due date, story points, importance buckets, and tags.

  • Adds filter types and view config extensions in apps/mcp/src/types/index.ts, apps/web/src/lib/interaction-api.ts, services/api/internal/domain/sprint/entity.go, and services/api/internal/domain/task/repository.go.
  • Builds a new filter panel in view-settings-panel.tsx and resolves view filters to query params in interaction-layout.tsx.
  • Parses new query params and resolves custom-field types server-side in task_handler.go.
  • Applies JSONB predicates in task_repository.go, including guarded casts for invalid stored numeric/date values.
  • Adds unit and E2E tests covering custom-field filters, invalid stored values, and field-key reuse.

Inline findings

services/api/internal/repository/postgres/task_repository.go:666 — The date guard regex only anchors at the start, so values such as "2024-01-15T12:00:00Z" or "2024-01-15garbage" match and then fail on ::date, aborting the whole query. Anchor the pattern (and consider rejecting invalid calendar dates such as "2024-02-30") so invalid stored values are excluded rather than crashing.

services/api/internal/transport/http/handler/task_handler.go:406-407 — Custom-field after/before values are passed through without isValidDateString, unlike start_date_after/due_date_before. A malformed date in custom_field_filters therefore returns HTTP 500 from the Postgres cast error instead of HTTP 400. Validate these dates inside parseCustomFieldFilters when cf.FieldType == "date".

services/api/internal/repository/postgres/task_repository.go:519due_date/start_date are timestamp with time zone columns, but the filter bound is a bare date string. due_date <= '2024-06-01' is only true up to midnight on June 1, so tasks later that day are excluded despite the <=. Consider casting the column to date or adjusting the bound to the end of the selected day so the range matches user expectations.

Pullfrog  | View workflow run | via Pullfrog | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@pullfrog

pullfrog Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Note: I attempted to submit the above review through pullfrog_create_pull_request_review (request-changes), but the Pullfrog MCP endpoint repeatedly returned "request_changes with no body or comments — nothing to block on" even when body/comments were provided. Please treat #286 (comment) as the request-changes review for this PR.

Pullfrog  | View workflow run | via Pullfrog | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@pullfrog

pullfrog Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

@pullfrog[bot] cannot successfully call pullfrog_create_pull_request_review in this run; every invocation is rejected as having "no body or comments" despite body/comments being supplied. A human or privileged run is needed to convert #286 (comment) into a formal PR review. I am stopping here to avoid further noise.

Pullfrog  | View workflow run | via Pullfrog | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes — this run covered the one new fix commit and the incremental delta it introduces.

  • End-anchored customFieldDateExpr regex — suffix-laden values like 2024-01-01T... no longer pass the guard and crash on ::date.
  • Capped importance bucket max at 2147483647 — prevents an integer-overflow error when getImportanceBucketBounds is bound directly into SQL BETWEEN.

ℹ️ Custom-field date filter inputs remain unvalidated at the API boundary

The handler checks that custom_field_filters is valid JSON, but it does not validate that each date field's after/before strings are well-formed dates. The repository-level regex guard now prevents the previous SQLSTATE 22007 crash, so malformed requests silently return an empty result set instead of a 400. Consider aligning this with start_date_*/due_date_*, which already return CodeBadRequest.

ℹ️ Built-in date filters compare timestamp columns against midnight date bounds

start_date and due_date are stored as timestamps, while the new *_after and *_before params accept YYYY-MM-DD. A task due at mid-day on June 15 will be excluded by due_date_before=2024-06-15 because the comparison is against midnight. Decide whether "before the 15th" should mean before midnight at the start or end of the day.

Pullfrog  | Fix it ➔View workflow run | Using Kimi K2 (free via Pullfrog for OSS) | 𝕏

@pikann
pikann merged commit 7b25486 into master Jul 17, 2026
6 checks passed
@pikann
pikann deleted the feature/add-support-for-custom-field-filter branch July 17, 2026 17:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Filtering with custom fields

1 participant