Add boolean custom field filters to member overview closes #309 #362

Merged
simon merged 15 commits from feature/filter-boolean-custom-fields into main 2026-01-23 14:53:08 +01:00
Owner

Description of the implemented changes

The changes were:

  • Bugfixing
  • New Feature
  • Breaking Change
  • Refactoring

Implementation of filters for Boolean Custom Fields in the member overview. The existing PaymentFilterComponent has been extended to a generic MemberFilterComponent that combines payment filters (All/Paid/Unpaid) and Boolean Custom Field filters (All/True/False).

What has been changed?

Backend (lib/mv_web/live/member_live/index.ex)

  • State Management: New assign boolean_custom_field_filters initialized as a map
  • URL Parameter Parsing: Function maybe_update_boolean_filters/2 parses bf_<id>=true|false parameters with whitelisting and security validation
  • Filter Logic:
    • get_boolean_custom_field_value/2 extracts boolean values from various formats (Ash.Union, Map)
    • apply_boolean_custom_field_filters/3 filters members by Boolean Custom Field values with AND logic
    • Integration into load_members/1 after payment filter
  • Loading Boolean Custom Fields: In mount/3, all Boolean Custom Fields are loaded and sorted
  • Event Handling: handle_info({:boolean_filter_changed, ...}) processes filter changes and updates URL
  • URL Building: build_query_params/6 extended to include boolean filter parameters
  • Security: DoS protection (max. 50 filters), UUID validation, whitelisting of Custom Field IDs

Frontend (lib/mv_web/live/components/member_filter_component.ex)

  • Component Extended: Renamed and extended from PaymentFilterComponent to MemberFilterComponent
  • UI Structure:
    • Payment filter at top (All/Paid/Unpaid)
    • Divider
    • Boolean Custom Fields below with scrollbar (max-h-60 overflow-y-auto) for many fields
    • Each Boolean Field with three options: All/True/False (displayed as Yes/No)
  • Event Handling:
    • handle_event("update_filters", ...) processes both filter types
    • handle_event("reset_filters", ...) resets all filters
  • Button Label: Shows active filter names (truncated for long names)
  • Badge: Shows count of active boolean filters

Integration (lib/mv_web/live/member_live/index.html.heex)

  • Component integrated with props: cycle_status_filter, boolean_custom_fields, boolean_filters, member_count

Constants (lib/mv/constants.ex)

  • New constants: boolean_filter_prefix(), max_boolean_filters(), max_uuid_length()

Tests

  • Unit Tests (test/mv_web/components/member_filter_component_test.exs):

    • Rendering of payment and boolean filters
    • Event handling for filter changes
    • Button label and badge logic
    • Scrollbar with many custom fields
  • Integration Tests (test/mv_web/member_live/index_test.exs):

    • State management and URL parameter parsing
    • Filter logic with various scenarios (true/false/multiple filters)
    • Edge cases: deleted custom fields, invalid URLs, nil values
    • Security: DoS protection, UUID validation
    • Performance test with 150 members
    • Combination with payment filter and search query

Definition of Done

Code Quality

  • No new technical depths
  • Linting passed
  • Documentation is added were needed
    • All new functions have docstrings
    • Security aspects documented
    • Code comments for complex logic

Accessibility

  • New elements are properly defined with html-tags
    • Semantic HTML elements (fieldset, label, button)
    • role="dialog" for dropdown panel
  • Colour contrast follows WCAG criteria
    • Uses DaisyUI classes with sufficient contrast
  • Aria labels are added when needed
    • aria-label for filter button, dropdown panel, fieldsets
    • aria-haspopup="true" and aria-expanded for dropdown button
    • aria-label for Yes/No buttons
  • Everything is accessible by keyboard
    • All filter options are radio inputs with labels
    • Tab navigation works
    • Escape key closes dropdown
  • Tab-Order is comprehensible
    • Logical order: Button → Payment Filter → Boolean Filters → Reset/Close Buttons
  • All interactive elements have a visible focus
    • has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-primary on all labels

Testing

  • Tests for new code are written
    • 30+ new tests covering all functionality
    • Unit tests for component
    • Integration tests for LiveView
    • Edge cases covered
  • All tests pass
    • All tests successfully executed
  • axe-core dev tools show no critical or major issues

Additional Notes

Security Considerations

  • DoS Protection: Maximum number of boolean filters per request limited to 50
  • Input Validation:
    • UUID format validation for Custom Field IDs
    • Whitelisting of allowed filter values ("true"/"false")
    • Whitelisting of existing Boolean Custom Fields
    • Length validation for UUIDs (max. 36 characters)
  • URL Parameters: All user inputs are validated, no raw values are passed to filter functions

Performance

  • Filter logic runs in-memory after loading members (same as payment filter)
  • Performance test with 150 members successful (< 1 second)
  • Scrollbar for many Boolean Custom Fields for better UX

Backward Compatibility

  • Payment filter continues to work as before
  • No breaking changes for existing functionality
  • URL parameters for payment filter remain unchanged

UI/UX

  • Dropdown stays open when changing filters (better UX for multiple filter changes)
  • Button label shows active filter names (truncated for long names)
  • Badge shows count of active boolean filters
  • Scrollbar for many Boolean Custom Fields (>10)
  • Visual separation between payment and boolean filters
## Description of the implemented changes The changes were: - [x] Bugfixing - [x] New Feature - [ ] Breaking Change - [ ] Refactoring Implementation of filters for Boolean Custom Fields in the member overview. The existing `PaymentFilterComponent` has been extended to a generic `MemberFilterComponent` that combines payment filters (All/Paid/Unpaid) and Boolean Custom Field filters (All/True/False). ## What has been changed? ### Backend (`lib/mv_web/live/member_live/index.ex`) - **State Management**: New assign `boolean_custom_field_filters` initialized as a map - **URL Parameter Parsing**: Function `maybe_update_boolean_filters/2` parses `bf_<id>=true|false` parameters with whitelisting and security validation - **Filter Logic**: - `get_boolean_custom_field_value/2` extracts boolean values from various formats (Ash.Union, Map) - `apply_boolean_custom_field_filters/3` filters members by Boolean Custom Field values with AND logic - Integration into `load_members/1` after payment filter - **Loading Boolean Custom Fields**: In `mount/3`, all Boolean Custom Fields are loaded and sorted - **Event Handling**: `handle_info({:boolean_filter_changed, ...})` processes filter changes and updates URL - **URL Building**: `build_query_params/6` extended to include boolean filter parameters - **Security**: DoS protection (max. 50 filters), UUID validation, whitelisting of Custom Field IDs ### Frontend (`lib/mv_web/live/components/member_filter_component.ex`) - **Component Extended**: Renamed and extended from `PaymentFilterComponent` to `MemberFilterComponent` - **UI Structure**: - Payment filter at top (All/Paid/Unpaid) - Divider - Boolean Custom Fields below with scrollbar (`max-h-60 overflow-y-auto`) for many fields - Each Boolean Field with three options: All/True/False (displayed as Yes/No) - **Event Handling**: - `handle_event("update_filters", ...)` processes both filter types - `handle_event("reset_filters", ...)` resets all filters - **Button Label**: Shows active filter names (truncated for long names) - **Badge**: Shows count of active boolean filters ### Integration (`lib/mv_web/live/member_live/index.html.heex`) - Component integrated with props: `cycle_status_filter`, `boolean_custom_fields`, `boolean_filters`, `member_count` ### Constants (`lib/mv/constants.ex`) - New constants: `boolean_filter_prefix()`, `max_boolean_filters()`, `max_uuid_length()` ### Tests - **Unit Tests** (`test/mv_web/components/member_filter_component_test.exs`): - Rendering of payment and boolean filters - Event handling for filter changes - Button label and badge logic - Scrollbar with many custom fields - **Integration Tests** (`test/mv_web/member_live/index_test.exs`): - State management and URL parameter parsing - Filter logic with various scenarios (true/false/multiple filters) - Edge cases: deleted custom fields, invalid URLs, nil values - Security: DoS protection, UUID validation - Performance test with 150 members - Combination with payment filter and search query ## Definition of Done ### Code Quality - [x] No new technical depths - [x] Linting passed - [x] Documentation is added were needed - All new functions have docstrings - Security aspects documented - Code comments for complex logic ### Accessibility - [x] New elements are properly defined with html-tags - Semantic HTML elements (`fieldset`, `label`, `button`) - `role="dialog"` for dropdown panel - [x] Colour contrast follows WCAG criteria - Uses DaisyUI classes with sufficient contrast - [x] Aria labels are added when needed - `aria-label` for filter button, dropdown panel, fieldsets - `aria-haspopup="true"` and `aria-expanded` for dropdown button - `aria-label` for Yes/No buttons - [x] Everything is accessible by keyboard - All filter options are radio inputs with labels - Tab navigation works - Escape key closes dropdown - [x] Tab-Order is comprehensible - Logical order: Button → Payment Filter → Boolean Filters → Reset/Close Buttons - [x] All interactive elements have a visible focus - `has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-primary` on all labels ### Testing - [x] Tests for new code are written - 30+ new tests covering all functionality - Unit tests for component - Integration tests for LiveView - Edge cases covered - [x] All tests pass - All tests successfully executed - [x] axe-core dev tools show no critical or major issues ## Additional Notes ### Security Considerations - **DoS Protection**: Maximum number of boolean filters per request limited to 50 - **Input Validation**: - UUID format validation for Custom Field IDs - Whitelisting of allowed filter values ("true"/"false") - Whitelisting of existing Boolean Custom Fields - Length validation for UUIDs (max. 36 characters) - **URL Parameters**: All user inputs are validated, no raw values are passed to filter functions ### Performance - Filter logic runs in-memory after loading members (same as payment filter) - Performance test with 150 members successful (< 1 second) - Scrollbar for many Boolean Custom Fields for better UX ### Backward Compatibility - Payment filter continues to work as before - No breaking changes for existing functionality - URL parameters for payment filter remain unchanged ### UI/UX - Dropdown stays open when changing filters (better UX for multiple filter changes) - Button label shows active filter names (truncated for long names) - Badge shows count of active boolean filters - Scrollbar for many Boolean Custom Fields (>10) - Visual separation between payment and boolean filters
simon added 9 commits 2026-01-21 01:20:04 +01:00
test: add tdd tests for custom boolean field filter logic
Some checks reported errors
continuous-integration/drone/push Build was killed
d65da2f498
feat: implement filter logic for boolean ustom fields
Some checks failed
continuous-integration/drone/push Build is failing
ff8b29cffe
Merge branch 'main' into feature/filter-boolean-custom-fields
Some checks failed
continuous-integration/drone/push Build is failing
01dea8bb8b
refactor: fix credo issues
All checks were successful
continuous-integration/drone/push Build is passing
fbf3b64192
feat: load boolean custom fields
All checks were successful
continuous-integration/drone/push Build is passing
1011b94acf
feat: add new filter component to members view
All checks were successful
continuous-integration/drone/push Build is passing
f996aee6b2
test: add more filter component tests
Some checks failed
continuous-integration/drone/push Build is failing
4b67039a78
simon added 1 commit 2026-01-21 01:24:55 +01:00
fix: credo warning
All checks were successful
continuous-integration/drone/push Build is passing
a92f503752
simon added 2 commits 2026-01-23 14:23:11 +01:00
simon added 1 commit 2026-01-23 14:34:07 +01:00
fix: failing test
Some checks failed
continuous-integration/drone/push Build is failing
20c96123e1
simon added 1 commit 2026-01-23 14:41:54 +01:00
Merge branch 'main' into feature/filter-boolean-custom-fields
Some checks failed
continuous-integration/drone/push Build is failing
672b4a8250
simon added 1 commit 2026-01-23 14:48:43 +01:00
Fix: Ensure members are loaded in handle_params when signature unchanged
All checks were successful
continuous-integration/drone/push Build is passing
1b44730b95
simon changed title from WIP: Add boolean custom field filters to member overview closes #309 to Add boolean custom field filters to member overview closes #309 2026-01-23 14:49:11 +01:00
simon merged commit b6992f8488 into main 2026-01-23 14:53:08 +01:00
simon deleted branch feature/filter-boolean-custom-fields 2026-01-23 14:53:10 +01:00
Sign in to join this conversation.
No description provided.