Replace after_action hooks with after_transaction to ensure async tasks only run after successful commit. Extract common cycle generation logic into handle_cycle_generation/2 to reduce duplication. Add structured error logging with context.
Remove paid field from Member resource, database migration,
tests, seeds, and UI. This field is no longer needed as payment
status is now tracked via membership fee cycles.
- Fix get_last_completed_cycle to find most recent completed cycle
- Fix create_cycle helpers to delete auto-generated cycles first
- Fix Ash.destroy return value handling
- Fix form selectors to use specific IDs
- Fix URL parameter names for filters
- Fix Ash.read_one return value expectations in tests
- Replace Enum.map |> Enum.join with Enum.map_join for efficiency
- Extract helper functions to reduce nesting depth from 4 to 2
- Rename is_current_cycle? to current_cycle? following Elixir conventions
- Implemented regenerate_cycles_on_type_change helper in Member resource
- Cycles that haven't ended yet (cycle_end >= today) are deleted and regenerated
- Paid and suspended cycles remain unchanged (not deleted)
- CycleGenerator reloads member with new membership_fee_type_id
- Adjusted tests to work with current cycles only (no future cycles)
- All integration tests passing
Phase 4 completed: Cycle regeneration on type change
- Log warnings when cycle generation fails in Member create/update
- Extract generate_fn to reduce code duplication
- Improves debuggability of silent failures
- Replace Application.get_env(:mv, :env) with :sql_sandbox config
- Remove redundant :env config from test.exs
- More explicit and less error-prone for test environment detection
- Escape SQL LIKE wildcards (% and _) to prevent pattern injection
- Limit search query length to 100 characters
- Apply sanitization in both :search action and linking filters
- FTS and fuzzy search use unsanitized query (wildcards not special there)
Custom field LIKE queries on JSONB are expensive (no index).
User linking only needs name/email search for autocomplete.
Custom fields are still searchable via main member search (uses FTS index).
Remove unnecessary credo:disable as function complexity is now acceptable.
The fields parameter was accepted but never used in the :search action.
Simplify API to only accept the query parameter.
Update @doc to reflect the actual functionality.
Explain the two-tier matching approach:
- % operator with server-wide threshold (0.3) for fast index scans
- similarity functions with configurable threshold (0.2) for edge cases
Add rationale for threshold value based on German name testing
- Replace 4 LIKE checks with 2 in build_custom_field_filter
- Simplify CASE blocks in migration trigger functions
- ->> operator always returns text, no need for -> + ::text fallback
- Performance improvement: 50% fewer LIKE operations
- Add @spec for public functions in Member and UserLive.Form
- Replace magic numbers with module constants:
- @member_search_limit = 10
- @default_similarity_threshold = 0.2
- Add comprehensive @doc for filter_by_email_match and fuzzy_search