- Add pattern="[0-9]+(\.[0-9]{1,2})?" to prevent invalid input
- Browser now validates number format before submission
- Improves UX by catching errors earlier
- Change get_existing_form_values to read from form[:field].value
- This ensures current form state is preserved when only interval changes
- Fixes issue where name and amount were cleared on interval selection
- Add current cycle status calculation and display
- Show both Last Cycle and Current Cycle status badges
- Replace single Status field with two separate fields
- Merge existing form values with new params to prevent field loss
- Add get_existing_form_values helper to extract current form state
- Fixes issue where name and amount were cleared when selecting interval
- Add Payment Data section showing membership fee amount, interval, and last cycle status
- Use real membership fee type data instead of mockup
- Calculate last cycle status from loaded cycles
Extract nested conditionals into separate helper functions:
- check_amount_change/2 and related helpers in MembershipFeeTypeLive.Form
- check_interval_change/2 and related helpers in MemberLive.Form
This reduces nesting depth from 3 to 2, improving code readability.
- Add list view showing name, amount, interval, member count
- Add create/edit forms for membership fee types
- Gray out interval field on edit (immutable)
- Show warning on amount change with impact information
- Prevent deletion if type is in use
- Add membership fee type selection in member create/edit form
- Show warning if different interval selected
- Filter available types to same interval only
- Add status column showing last completed or current cycle status
- Add toggle to switch between last/current cycle view
- Add color coding (green/red/gray) for paid/unpaid/suspended
- Add filters for unpaid cycles in last/current cycle
- Efficiently load cycles to avoid N+1 queries
MembershipFeeHelpers: formatting functions for currency, intervals, cycles
MembershipFeeStatus: helper for loading and determining cycle status in member list
Change validation to fail closed instead of fail open when types cannot
be loaded. This prevents inconsistent data states and provides clearer
error messages to users.
- 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
- Remove Process.sleep calls from integration tests (tests run synchronously in SQL sandbox)
- Improve error handling: membership_fee_type_not_found now returns changeset error instead of just logging
- Clarify partial_failure documentation: successful_cycles are not persisted on rollback
- Update documentation: joined_at → join_date, left_at → exit_date
- Document PostgreSQL advisory locks per member (not whole table lock)
- Document gap handling: explicitly deleted cycles are not recreated
- Use return_notifications?: true when creating cycles within transaction
- Collect notifications and send them after transaction commits
- Prevents 'Missed notifications' warnings in test output
- Notifications are now properly sent via Ash.Notifier.notify/1
- Change algorithm to start from last existing cycle instead of start_date
- Deleted cycles (gaps) are no longer automatically filled
- Add test to verify gaps remain unfilled
- Update documentation to clarify gap handling behavior
- Handle Task crashes in async_stream with {:exit, reason}
- Return {:error, {:partial_failure, successes, errors}} when some cycles fail
- Previously returned {:ok, successful} even on partial failures
- Improves debuggability and allows callers to handle partial failures
- Remove exit_date filter from generate_cycles_for_all_members query
- Inactive members now get cycles generated up to their exit_date
- Add tests for inactive member processing and exit_date boundary
- Document exit_date == cycle_start behavior (cycle still generated)
- Add warning logging for unexpected errors (not missing prerequisites)
- Use CalendarCycles.interval() type instead of generic atom()
- Update moduledoc to reflect actual usage (no where clause needed)
- 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