Commit graph

263 commits

Author SHA1 Message Date
45c9b81983 fix(import): collapse duplicate fee-type warnings into a bounded list 2026-06-03 02:37:12 +02:00
118b9f8d57 perf(import): reuse auto-created groups across import chunks 2026-06-03 02:32:15 +02:00
68a1a9530a feat(import): confirm column mapping in a preview before importing members 2026-06-03 02:25:50 +02:00
00e1624ee4 feat(import): assign groups and fee types to imported members, creating missing groups 2026-06-03 02:15:54 +02:00
a4a34cab3a feat(import): resolve import group and fee-type names against existing records 2026-06-03 02:10:33 +02:00
95c7bf7a15 feat(import): recognize group and fee-type columns and always ignore fee-status 2026-06-03 02:01:09 +02:00
5c5fd56749 fix(export): emit date custom-field values as ISO-8601 for re-import 2026-06-03 01:54:49 +02:00
9a14cedc14 fix(repo): define all_tenants/0 as empty for non-multitenant schema
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/promote/production Build is passing
2026-06-02 12:26:35 +02:00
b5756d8e00 refactor(vereinfacht): gate retry skipping on runtime sandbox flag
The compile-time Mix.env() comparison folded to an always-false literal under analysis. sql_sandbox?/0 reads runtime config (true only in test) and works in releases where Mix is unavailable, preserving the fast-fail-no-retry behavior in tests.
2026-06-02 12:23:04 +02:00
6a4a99f638 refactor(types): drop guards and clauses that can never succeed 2026-06-02 12:11:59 +02:00
2db467d5d1 fix(pdf-export): match DateTime.from_iso8601 three-tuple when formatting cells
DateTime.from_iso8601/1 returns {:ok, datetime, offset}, so the two-tuple clauses never matched and datetime cells fell through to the naive-parse fallback. Matching the real shape routes them through the intended DateTime path; UTC values render identically.
2026-06-02 12:04:37 +02:00
c41d24113f fix(import): return readable string for unreadable upload errors
File.read/1 only yields posix atoms, so the File.Error and bare-reason branches were unreachable, and :file.format_error/1 returns a charlist rather than a String. Normalize the error to a binary so it interpolates correctly in flash messages.
2026-06-02 12:00:38 +02:00
05f66ccf74 refactor(types): remove dead catch-all clauses unreachable per success typing 2026-06-02 11:56:44 +02:00
848f0cd013 refactor(types): bind intentionally discarded side-effecting results 2026-06-02 11:42:57 +02:00
5352a635c6 refactor(release): bind discarded results of side-effecting release tasks 2026-06-02 11:33:14 +02:00
fd8e6ac178 refactor(types): reconcile @specs with their success typings 2026-06-02 11:25:03 +02:00
ba66bc15db fix(auth): trigger RP-initiated logout at OIDC provider 2026-06-01 19:59:52 +02:00
ddd4a9a878 feat(date-filter): introduce DateFilter module with URL codec and Ash query expressions 2026-05-20 16:24:08 +02:00
a12888de2f Improve member view table behavior+style, fix config settings (#493)
All checks were successful
continuous-integration/drone/push Build is passing
## 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>
2026-05-08 15:04:53 +02:00
2bb01bd201 Improve UX of join requests and fix minor bugs (#492)
All checks were successful
continuous-integration/drone/push Build is passing
## Description of the implemented changes
The changes were:
- [x] Bugfixing
- [x] New Feature
- [ ] Breaking Change
- [ ] Refactoring

This PR improves the join-request flow and presentation quality, fixes several data-display issues in join/join-request screens, and adds a usability improvement in global settings (directly opening the join link). It also includes dependency updates and changelog maintenance.

## What has been changed?
- Join form (`JoinLive`) now renders inputs based on actual field types (including checkbox/date/number/email behavior instead of generic text-only handling).
- Join form custom-field labels are resolved from configured custom fields (fallback remains safe if lookup fails).
- Join-request details page (`JoinRequestLive.Show`) now:
  - resolves and shows custom field names instead of raw IDs,
  - formats boolean-like values (`on/true/1`, `off/false/0`) as localized `Yes/No`,
  - formats ISO date strings for better readability,
  - keeps legacy field handling while improving output consistency.
- Join-request detail layout was improved semantically and visually (`dl/dt/dd` structure for label/value rows).
- Global settings page now includes an **Open** button for the join URL (`target="_blank"`, `rel="noopener noreferrer"`, ARIA label).
- Added/updated tests around:
  - join field type rendering,
  - custom field labels in join-request views,
  - related auth/global-settings behavior.
- Updated translations (`default.pot`, `en`, `de`) for new UI strings.
- Updated dependencies/tooling (`mix.lock`, `mix.exs`, CI/renovate-related updates).
- Updated `CHANGELOG.md` entries for unreleased changes.

## 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
- [ ] All tests pass
- [ ] axe-core dev tools show no critical or major issues

## Additional Notes
- Reviewer focus areas:
  - `lib/mv_web/live/join_live.ex`: input type derivation and custom field lookup strategy (`authorize?: false` read path used intentionally for field metadata).
  - `lib/mv_web/live/join_request_live/show.ex`: value-formatting logic (especially backward compatibility for legacy `form_data` payloads).
  - `lib/mv_web/live/global_settings_live.ex`: external-link behavior and accessibility attributes.
- The branch also contains dependency update commits; please review lockfile and CI-related changes separately from functional join/join-request changes.

Reviewed-on: #492
Co-authored-by: Simon <s.thiessen@local-it.org>
Co-committed-by: Simon <s.thiessen@local-it.org>
2026-05-06 14:34:42 +02:00
2e727aec9c
fix: remove illegal reference and update test
Some checks failed
continuous-integration/drone/push Build is failing
2026-05-04 17:37:59 +02:00
c48ac2f432 harden env handling (#481)
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #481
Co-authored-by: Simon <s.thiessen@local-it.org>
Co-committed-by: Simon <s.thiessen@local-it.org>
2026-04-08 10:40:22 +02:00
f8a3cc4c47 Run seeds only once (#475)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing
continuous-integration/drone/tag Build is passing
## Description of the implemented changes
The changes were:
- [ ] Bugfixing
- [x] New Feature
- [ ] Breaking Change
- [x] Refactoring

**Seeds run only on first startup.** On every application start (e.g. `just run`, Docker entrypoint), seed scripts are still invoked, but they exit immediately when the admin user already exists. This avoids duplicate seed data (e.g. join requests), keeps startup fast after the first run, and works the same in dev and production.

## What has been changed?

- **`lib/mv/release.ex`**
  - Added `bootstrap_seeds_applied?/0`: returns whether the admin user (from `ADMIN_EMAIL` or default `admin@localhost`) exists. We check the admin *user*, not the Admin *role*, so we do not skip when only migrations have run (migrations can create the Admin role for the system actor).
  - `run_seeds/0`: if `bootstrap_seeds_applied?()` is true, prints “Seeds already applied (admin user exists). Skipping.” and returns without running bootstrap or dev seeds; otherwise unchanged behaviour.
  - Module docs updated for the new function and the skip behaviour.

- **`priv/repo/seeds.exs`**
  - Ensures the app is started (`Application.ensure_all_started(:mv)`).
  - If `Mv.Release.bootstrap_seeds_applied?()` is true, prints the same skip message and does not run bootstrap or dev seeds; otherwise runs as before (bootstrap + dev seeds in dev/test).
  - Comment at the top updated to describe the skip behaviour.

- **Documentation**
  - `CODE_GUIDELINES.md` §1.2.1: seeds run on every start but exit early when already applied; mentions `bootstrap_seeds_applied?/0`.
  - `docs/admin-bootstrap-and-oidc-role-sync.md`: run_seeds skips when admin user exists; description of `run_seeds/0` updated.
  - `CHANGELOG.md` [Unreleased]: new “Seeds run only when needed” entry under Changed.

## Definition of Done
### Code Quality
- [x] No new technical depths
- [x] Linting passed
- [x] Documentation is added where needed

### Accessibility
- [x] New elements are properly defined with html-tags *(no new UI)*
- [x] Colour contrast follows WCAG criteria *(no new UI)*
- [x] Aria labels are added when needed *(no new UI)*
- [x] Everything is accessible by keyboard *(no new UI)*
- [x] Tab-Order is comprehensible *(no new UI)*
- [x] All interactive elements have a visible focus *(no new UI)*

### Testing
- [x] Tests for new code are written *(existing seeds and release tests cover behaviour; idempotency test still passes when second run skips)*
- [x] All tests pass
- [x] axe-core dev tools show no critical or major issues *(no UI changes)*

## Additional Notes

- **Review focus:** Logic in `Mv.Release` and `priv/repo/seeds.exs`; the “already applied” check is a single DB read for the admin user. On failure (e.g. DB down), `bootstrap_seeds_applied?/0` returns `false`, so seeds run (safe for first deploy).
- **Suggested check:** Run `mix test test/seeds_test.exs test/mv/release_test.exs` to confirm seeds and release behaviour.

Reviewed-on: #475
Co-authored-by: Simon <s.thiessen@local-it.org>
Co-committed-by: Simon <s.thiessen@local-it.org>
2026-03-16 19:27:31 +01:00
c381b86b5e Improve oidc only mode (#474)
All checks were successful
continuous-integration/drone/push Build is passing
## Description of the implemented changes
The changes were:
- [x] Bugfixing
- [x] New Feature
- [ ] Breaking Change
- [x] Refactoring

**OIDC-only mode improvements and UX tweaks (success toasts, unauthenticated redirect).**

## What has been changed?

### OIDC-only mode (new feature)
- **Admin settings:** "Only OIDC sign-in" is an immediate toggle at the top of the OIDC section (no save button). Enabling it also turns off "Allow direct registration". When OIDC-only is on, the registration checkbox is disabled and shows a tooltip (DaisyUI `<.tooltip>`).
- **Backend:** Password sign-in is forbidden via Ash policy (`OidcOnlyActive` check). Password registration is blocked via validation `OidcOnlyBlocksPasswordRegistration`. New plug `OidcOnlySignInRedirect`: when OIDC-only and OIDC are configured, GET `/sign-in` redirects to the OIDC flow; GET `/auth/user/password/sign_in_with_token` is rejected with redirect + flash. `AuthController.success/4` also rejects password sign-in when OIDC-only.
- **Tests:** GlobalSettingsLive (OIDC-only UI), AuthController (redirect and password sign-in rejection), User authentication (register_with_password blocked when OIDC-only).

### UX / behaviour (no new feature flag)
- **Success toasts:** Success flash messages auto-dismiss after 5 seconds via JS hook `FlashAutoDismiss` and optional `auto_clear_ms` on `<.flash>` (used for success in root layout and `flash_group`).
- **Unauthenticated users:** Redirect to sign-in without the "You don't have permission to access this page" flash; that message is only shown to logged-in users who lack access. Logic in `LiveHelpers` and `CheckPagePermission` plug; test updated accordingly.

### Other
- Layouts: comment about unprocessed join-request count no longer uses "TODO" (Credo).
- Gettext: German translation for "Home" (Startseite); POT/PO kept in sync.
- CHANGELOG: Unreleased section updated with the above.

## Definition of Done
### Code Quality
- [x] No new technical depths
- [x] Linting passed
- [x] Documentation is added where needed (module docs, comments where non-obvious)

### Accessibility
- [x] New elements are properly defined with html-tags (labels, aria-label on checkboxes)
- [x] Colour contrast follows WCAG criteria (unchanged)
- [x] Aria labels are added when needed (e.g. oidc-only and registration checkboxes)
- [x] Everything is accessible by keyboard (toggles and buttons unchanged)
- [x] Tab-Order is comprehensible
- [x] All interactive elements have a visible focus (existing patterns)

### Testing
- [x] Tests for new code are written (OIDC-only UI, auth controller, user auth; SMTP config builder and mailer)
- [x] All tests pass
- [ ] axe-core dev tools show no critical or major issues (not re-run for this PR; suggest spot-check on settings and sign-in)

## Additional Notes
- **OIDC-only:** When the `OIDC_ONLY` env var is set, the toggle is read-only and shows "(From OIDC_ONLY)". When OIDC is not configured, the toggle is disabled.
- **Invalidation:** Enabling OIDC-only sets `registration_enabled: false` in one update; disabling OIDC-only only updates `oidc_only` (registration left as-is).
- **Review focus:** Plug order in router (OidcOnlySignInRedirect), policy/validation order in User, and that all OIDC-only paths (form, plug, controller) stay consistent.

Reviewed-on: #474
Co-authored-by: Simon <s.thiessen@local-it.org>
Co-committed-by: Simon <s.thiessen@local-it.org>
2026-03-16 19:09:07 +01:00
e8f27690a1
refactor: unify smtp config logic
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/promote/production Build is failing
2026-03-16 14:23:46 +01:00
e95c1d6254
fix: repaired smtp configuration for port 587
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-16 14:00:23 +01:00
349cee0ce6
refactor: review remarks
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-13 17:55:17 +01:00
942f2afd9e
refactor: adress review
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-12 15:29:54 +01:00
a4f3aa5d6f
feat: add smtp settings
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-12 13:39:48 +01:00
c4135308e6
test: add tests for smtp mailer config 2026-03-11 09:18:37 +01:00
28f97184b3 Merge branch 'main' into feature/308-web-form
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-11 02:05:13 +01:00
86d9242d83
feat: add approval ui for join requests
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-11 02:04:03 +01:00
f79c9ac515 Merge pull request 'add public join form' (#466) from feature/308-web-form into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #466
2026-03-10 23:08:26 +01:00
a9c61f703d
fix: resolve Mix.env at compile time in Vereinfacht client
Mix.env() is not available in production releases. Use module
attribute so it is only evaluated at compile time.
2026-03-10 20:17:26 +01:00
f1d0526209
feat: add join form
Some checks failed
continuous-integration/drone/push Build is failing
2026-03-10 18:25:17 +01:00
5deb102e45
refactor: adress review comments
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-09 18:54:40 +01:00
6385fbc831
feat: add join confirmation and mail templating
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-09 18:15:12 +01:00
ad6ef169ac
Merge remote-tracking branch 'origin/main' into feature/308-web-form
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing
2026-03-09 15:40:02 +01:00
d032f1ca0c
Run bootstrap seeds in production; add RUN_DEV_SEEDS support
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/promote/production Build is passing
2026-03-09 15:16:02 +01:00
2515a679b8
feat: add join request resource
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-09 14:44:45 +01:00
ae07e3efc2 Add filter prefix constants and shared FilterParams module
- Mv.Constants: group_filter_prefix/0, fee_type_filter_prefix/0
- MvWeb.MemberLive.Index.FilterParams: parse_in_not_in_value/1 for URL param parsing
2026-03-09 14:33:58 +01:00
fc7b035123
CSV export: robust apply_export_filters, single custom_field_ids_union, string boolean_filters, more tests
Some checks reported errors
continuous-integration/drone/push Build was killed
2026-03-04 21:15:54 +01:00
d71d5881cf
CSV export: apply cycle_status_filter and boolean_filters when exporting all 2026-03-04 21:15:54 +01:00
01b9ebd74b
Vereinfacht client: email normalization, multi-match warning, Bypass tests, doc note
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing
- Normalize email (trim + downcase) before filter lookup
- Log warning when API returns multiple contacts for same email
- Add Bypass tests for find_contact_by_email (query params, empty/single response parsing)
- Document vereinfacht_required_field? as legacy/unused in vereinfacht-api.md
- Add bypass dependency (dev+test) for HTTP stubbing
2026-03-04 20:55:59 +01:00
9f169b9835
Vereinfacht: sync country with finance contact API
All checks were successful
continuous-integration/drone/push Build is passing
2026-03-04 20:21:51 +01:00
96ca857e06
Vereinfacht API: use filter for contact lookup, drop extra required fields
- find_contact_by_email uses GET with filter[isExternal]=true and filter[email]
- vereinfacht_required_member_fields is now empty (API accepts minimal payload)
2026-03-04 20:21:50 +01:00
1ce9915c7d
Member/CycleGenerator: better delete_cycles errors; UUID-based advisory lock
delete_cycles returns first error for debugging. Advisory lock key
derived from member id (first 8 bytes of UUID hex) to reduce
phash2 collision risk; fallback to phash2 on invalid UUID.
2026-03-04 17:11:56 +01:00
7a8b069834
Fix Credo Design (AliasUsage): add aliases in lib
Add module aliases at top and use short names instead of
fully qualified nested modules across lib/.
2026-03-04 16:21:15 +01:00
81ce204502
Fix Credo Readability (strict)
- Max line length, implicit try, alias order, zero-arity defs
- String sigils, long comments split; redundant blank lines fixed
2026-03-04 16:21:14 +01:00
3187d408c5
OIDC: use Application config :oidc from runtime.exs for client secret in prod
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/promote/production Build is passing
2026-03-02 15:09:33 +01:00