## Description of the implemented changes
The changes were:
- [x] Bugfixing
- [x] New Feature
- [ ] Breaking Change
- [x] Refactoring
This PR standardizes interactive table behavior and improves settings robustness.
It makes the new hover/focus-visible row highlight the default for clickable tables, keeps sticky first-column behavior configurable (and optimized for member selection UX), and tightens SMTP source-of-truth handling so ENV-based and UI-based configuration do not conflict.
## What has been changed?
- Refactored `CoreComponents.table` to expose interaction state via `data-row-interactive` and moved default row hover/focus styling to CSS.
- Made the new row highlight behavior (`hover` + `:has(:focus-visible)`) the default for clickable zebra tables.
- Kept sticky-first-column as an explicit table option and preserved sticky-specific selection accent behavior.
- Updated member overview table usage to the sticky-first-column mode and refined scrolling behavior (table scrollbar within container, not page-coupled).
- Adjusted table-related tests to validate the new interaction contract (attribute/CSS-driven behavior instead of legacy ring classes).
- Improved SMTP config handling:
- clearer ENV-vs-Settings behavior (ENV-only mode when host env is set),
- read-only and warning behavior in global settings UI when required env keys are missing,
- updated related config/tests/docs.
- Updated docs and changelog (`CHANGELOG.md`, `DESIGN_GUIDELINES.md`, `CODE_GUIDELINES.md`, SMTP concept docs).
- Updated gettext catalogs (`default.pot`, `en`, `de`) for new/changed UI strings.
## Definition of Done
### Code Quality
- [x] No new technical depths
- [x] Linting passed
- [x] Documentation is added were needed
### Accessibility
- [x] New elements are properly defined with html-tags
- [x] Colour contrast follows WCAG criteria
- [x] Aria labels are added when needed
- [x] Everything is accessible by keyboard
- [x] Tab-Order is comprehensible
- [x] All interactive elements have a visible focus
### Testing
- [x] Tests for new code are written
- [x] All tests pass
- [ ] axe-core dev tools show no critical or major issues
## Additional Notes
- Branch includes 4 commits:
- `fix: make sure smtp can be set either via env or ui`
- `fix: make horizontal scrollbars sticky to bottom`
- `docs: update changelog`
- `feat: make checkbox column in member view sticky`
- Full fast suite passed (`mix test --exclude slow --exclude ui`): 2017 tests, 0 failures (plus expected non-failing warning logs in test output).
- Reviewer focus areas:
1. **Cross-table UX consistency** after moving row interaction styling to component/CSS contract.
2. **Sticky table behavior** (selection accent stripe, zebra background, keyboard focus visibility).
3. **SMTP precedence and UI constraints** in global settings when ENV mode is active.
4. **Regression risk in tests** that previously asserted ring-based row classes.
- No breaking API changes expected; behavior change is primarily visual/interaction-level and intentional.
Reviewed-on: #493
Co-authored-by: Simon <s.thiessen@local-it.org>
Co-committed-by: Simon <s.thiessen@local-it.org>
- find_contact_by_email uses GET with filter[isExternal]=true and filter[email]
- vereinfacht_required_member_fields is now empty (API accepts minimal payload)
- Add /admin/datafields (DatafieldsLive) for member and custom field config
- Remove Memberdata block from GlobalSettingsLive
- Router: drop /membership_fee_types, add new_fee_type and edit_fee_type under membership_fee_settings
- MembershipFeeSettingsLive: fee types table, collapsible examples; Index links updated
- PagePaths: admin_datafields, admin_import; remove membership_fee_types
- Sidebar: order and labels (Basic settings, Datafields, Membership fee settings, Import, Users, Roles)
- Gettext: German translations for sidebar and OIDC
- Tests: datafields and fee routes, permission and form tests updated
- Add oidc_* attributes to Setting, migration and Config helpers
- Secrets and OidcRoleSyncConfig read from Config (ENV overrides DB)
- GlobalSettingsLive: OIDC section with disabled fields when ENV set
- OIDC role sync tests use DataCase for DB access
- Rename AshAuthentication strategy from :oidc :rauthy to :oidc :oidc;
generated actions are now register_with_oidc / sign_in_with_oidc.
- Update config keys (:rauthy → :oidc) in dev.exs and runtime.exs.
- Update default_redirect_uri to /auth/user/oidc/callback everywhere.
- Rename Mv.Accounts helper functions accordingly.
- Update Mv.Secrets, AuthController, link_oidc_account_live and all tests.
- Update docker-compose.prod.yml, .env.example, README and docs.
IMPORTANT: OIDC providers must be updated to use the new redirect URI
/auth/user/oidc/callback instead of /auth/user/rauthy/callback.
Add tests for required validation, update_single_member_field, form
required map. Add street/postal_code/city to sync_contact when Vereinfacht configured.
- Use OidcRoleSyncContext for set_role_from_oidc_sync; document JWT peek risk.
- seed_admin without password sets Admin role on existing user (OIDC-only); update docs and test.
- Fix DE translation for 'access this page'; add get? true comment in User.
Register and sign-in call apply_admin_role_from_user_info; users in configured
admin group get Admin role, others get Mitglied. Internal User action + bypass policy.
Creates/updates admin user from ADMIN_EMAIL and ADMIN_PASSWORD or ADMIN_PASSWORD_FILE.
Idempotent; no fallback password in production. Called from docker entrypoint and seeds.
- Member update_member: on_missing :unrelate → :ignore (no unlink when :user omitted)
- Test: normal_user update linked member without :user keeps link
- Doc: unlink only explicit (user: nil), admin-only; Actor.admin?(nil) note
- Check: defense-in-depth for "user" string key
- Forbid on :user argument presence (not value) to block unlink via nil/empty
- Defensive nil actor handling; policy restricted to create/update only
- Test: Ash.load with actor; test non-admin cannot unlink via user: nil
- Docs: unlink behaviour and policy split
- PermissionSets: Role read :all for own_data, read_only, normal_user; admin keeps full CRUD
- Role resource: authorizers and policies with HasPermission
- Tests: role_policies_test.exs (read all, create/update/destroy admin only)
- Fix existing tests to pass actor or authorize?: false for Role operations
- groups-architecture: normal_user and admin can manage groups.
- roles-and-permissions: matrix and MembershipFeeCycle :linked for own_data.
- group_policies_test: update moduledoc.
- ActorPermissionSetIs check; bypass policy filters by member_id for own_data only.
- Admin with member_id still gets :all via HasPermission. Tests added.