docs: add translations and update development log (#168)

This commit is contained in:
Moritz 2025-11-20 15:57:10 +01:00
parent eaaa2f37dc
commit a67a2780b7
6 changed files with 338 additions and 33 deletions

19
CHANGELOG.md Normal file
View file

@ -0,0 +1,19 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- User-Member linking with fuzzy search autocomplete (#168)
- PostgreSQL trigram-based member search with typo tolerance
- WCAG 2.1 AA compliant autocomplete dropdown with ARIA support
- Bilingual UI (German/English) for member linking workflow
### Fixed
- Email validation false positive when linking user and member with identical emails (#168 Problem #4)
- Relationship data extraction from Ash manage_relationship during validation

View file

@ -1321,6 +1321,135 @@ end
--- ---
## Session: User-Member Linking UI Enhancement (2025-01-13)
### Feature Summary
Implemented user-member linking functionality in User Edit/Create views with fuzzy search autocomplete, email conflict handling, and accessibility support.
**Key Features:**
- Autocomplete dropdown with PostgreSQL Trigram fuzzy search
- Link/unlink members to user accounts
- Email synchronization between linked entities
- WCAG 2.1 AA compliant (ARIA labels)
- Bilingual UI (English/German)
### Technical Decisions
**1. Search Priority Logic**
Search query takes precedence over email filtering to provide better UX:
- User types → fuzzy search across all unlinked members
- Email matching only used for post-filtering when no search query present
**2. JavaScript Hook for Input Value**
Used minimal JavaScript (~6 lines) for reliable input field updates:
```javascript
// assets/js/app.js
window.addEventListener("phx:set-input-value", (e) => {
document.getElementById(e.detail.id).value = e.detail.value
})
```
**Rationale:** LiveView DOM patching has race conditions with rapid state changes in autocomplete components. Direct DOM manipulation via `push_event` is the idiomatic LiveView solution for this edge case.
**3. Fuzzy Search Implementation**
Combined PostgreSQL Full-Text Search + Trigram for optimal results:
```sql
-- FTS for exact word matching
search_vector @@ websearch_to_tsquery('simple', 'greta')
-- Trigram for typo tolerance
word_similarity('gre', first_name) > 0.2
-- Substring for email/IDs
email ILIKE '%greta%'
```
### Key Learnings
#### 1. Ash `manage_relationship` Internals
**Critical Discovery:** During validation, relationship data lives in `changeset.relationships`, NOT `changeset.attributes`:
```elixir
# During validation (manage_relationship processing):
changeset.relationships.member = [{[%{id: "uuid"}], opts}]
changeset.attributes.member_id = nil # Still nil!
# After action completes:
changeset.attributes.member_id = "uuid" # Now set
```
**Solution:** Extract member_id from both sources:
```elixir
defp get_member_id_from_changeset(changeset) do
case Map.get(changeset.relationships, :member) do
[{[%{id: id}], _opts}] -> id # New link
_ -> Ash.Changeset.get_attribute(changeset, :member_id) # Existing
end
end
```
**Impact:** Fixed email validation false positives when linking user+member with identical emails.
#### 2. LiveView + JavaScript Integration Patterns
**When to use JavaScript:**
- ✅ Direct DOM manipulation (autocomplete, input values)
- ✅ Browser APIs (clipboard, geolocation)
- ✅ Third-party libraries
**When NOT to use JavaScript:**
- ❌ Form submissions
- ❌ Simple show/hide logic
- ❌ Server-side data fetching
**Pattern:**
```elixir
socket |> push_event("event-name", %{key: value})
```
```javascript
window.addEventListener("phx:event-name", (e) => { /* handle */ })
```
#### 3. PostgreSQL Trigram Search
Requires `pg_trgm` extension with GIN indexes:
```sql
CREATE INDEX members_first_name_trgm_idx
ON members USING GIN(first_name gin_trgm_ops);
```
Supports:
- Typo tolerance: "Gret" finds "Greta"
- Partial matching: "Mit" finds "Mitglied"
- Substring: "exam" finds "example.com"
#### 4. Test-Driven Development for Bug Fixes
Effective workflow:
1. Write test that reproduces bug (should fail)
2. Implement minimal fix
3. Verify test passes
4. Refactor while green
**Result:** 355 tests passing, 100% backend coverage for new features.
### Files Changed
**Backend:**
- `lib/membership/member.ex` - `:available_for_linking` action with fuzzy search
- `lib/mv/accounts/user/validations/email_not_used_by_other_member.ex` - Relationship change extraction
- `lib/mv_web/live/user_live/form.ex` - Event handlers, state management
**Frontend:**
- `assets/js/app.js` - Input value hook (6 lines)
- `priv/gettext/**/*.po` - 10 new translation keys (DE/EN)
**Tests (NEW):**
- `test/membership/member_fuzzy_search_linking_test.exs`
- `test/accounts/user_member_linking_email_test.exs`
- `test/mv_web/user_live/form_member_linking_ui_test.exs`
### Deployment Notes
- **Assets:** Requires `cd assets && npm run build`
- **Database:** No migrations (uses existing indexes)
- **Config:** No changes required
---
## Conclusion ## Conclusion
This project demonstrates a modern Phoenix application built with: This project demonstrates a modern Phoenix application built with:

View file

@ -16,7 +16,7 @@
"comeonin": {:hex, :comeonin, "5.5.1", "5113e5f3800799787de08a6e0db307133850e635d34e9fab23c70b6501669510", [:mix], [], "hexpm", "65aac8f19938145377cee73973f192c5645873dcf550a8a6b18187d17c13ccdb"}, "comeonin": {:hex, :comeonin, "5.5.1", "5113e5f3800799787de08a6e0db307133850e635d34e9fab23c70b6501669510", [:mix], [], "hexpm", "65aac8f19938145377cee73973f192c5645873dcf550a8a6b18187d17c13ccdb"},
"credo": {:hex, :credo, "1.7.13", "126a0697df6b7b71cd18c81bc92335297839a806b6f62b61d417500d1070ff4e", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "47641e6d2bbff1e241e87695b29f617f1a8f912adea34296fb10ecc3d7e9e84f"}, "credo": {:hex, :credo, "1.7.13", "126a0697df6b7b71cd18c81bc92335297839a806b6f62b61d417500d1070ff4e", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "47641e6d2bbff1e241e87695b29f617f1a8f912adea34296fb10ecc3d7e9e84f"},
"crux": {:hex, :crux, "0.1.1", "94f2f97d2a6079ae3c57f356412bc3b307f9579a80e43f526447b1d508dd4a72", [:mix], [{:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: true]}], "hexpm", "e59d498f038193cbe31e448f9199f5b4c53a4c67cece9922bb839595189dd2b6"}, "crux": {:hex, :crux, "0.1.1", "94f2f97d2a6079ae3c57f356412bc3b307f9579a80e43f526447b1d508dd4a72", [:mix], [{:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: true]}], "hexpm", "e59d498f038193cbe31e448f9199f5b4c53a4c67cece9922bb839595189dd2b6"},
"db_connection": {:hex, :db_connection, "2.8.1", "9abdc1e68c34c6163f6fb96a96532272d13ad7ca45262156ae8b7ec6d9dc4bec", [], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a61a3d489b239d76f326e03b98794fb8e45168396c925ef25feb405ed09da8fd"}, "db_connection": {:hex, :db_connection, "2.8.1", "9abdc1e68c34c6163f6fb96a96532272d13ad7ca45262156ae8b7ec6d9dc4bec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a61a3d489b239d76f326e03b98794fb8e45168396c925ef25feb405ed09da8fd"},
"decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"},
"dns_cluster": {:hex, :dns_cluster, "0.2.0", "aa8eb46e3bd0326bd67b84790c561733b25c5ba2fe3c7e36f28e88f384ebcb33", [:mix], [], "hexpm", "ba6f1893411c69c01b9e8e8f772062535a4cf70f3f35bcc964a324078d8c8240"}, "dns_cluster": {:hex, :dns_cluster, "0.2.0", "aa8eb46e3bd0326bd67b84790c561733b25c5ba2fe3c7e36f28e88f384ebcb33", [:mix], [], "hexpm", "ba6f1893411c69c01b9e8e8f772062535a4cf70f3f35bcc964a324078d8c8240"},
"ecto": {:hex, :ecto, "3.13.3", "6a983f0917f8bdc7a89e96f2bf013f220503a0da5d8623224ba987515b3f0d80", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1927db768f53a88843ff25b6ba7946599a8ca8a055f69ad8058a1432a399af94"}, "ecto": {:hex, :ecto, "3.13.3", "6a983f0917f8bdc7a89e96f2bf013f220503a0da5d8623224ba987515b3f0d80", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1927db768f53a88843ff25b6ba7946599a8ca8a055f69ad8058a1432a399af94"},
@ -80,7 +80,7 @@
"telemetry_metrics": {:hex, :telemetry_metrics, "1.1.0", "5bd5f3b5637e0abea0426b947e3ce5dd304f8b3bc6617039e2b5a008adc02f8f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7b79e8ddfde70adb6db8a6623d1778ec66401f366e9a8f5dd0955c56bc8ce67"}, "telemetry_metrics": {:hex, :telemetry_metrics, "1.1.0", "5bd5f3b5637e0abea0426b947e3ce5dd304f8b3bc6617039e2b5a008adc02f8f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7b79e8ddfde70adb6db8a6623d1778ec66401f366e9a8f5dd0955c56bc8ce67"},
"telemetry_poller": {:hex, :telemetry_poller, "1.3.0", "d5c46420126b5ac2d72bc6580fb4f537d35e851cc0f8dbd571acf6d6e10f5ec7", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "51f18bed7128544a50f75897db9974436ea9bfba560420b646af27a9a9b35211"}, "telemetry_poller": {:hex, :telemetry_poller, "1.3.0", "d5c46420126b5ac2d72bc6580fb4f537d35e851cc0f8dbd571acf6d6e10f5ec7", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "51f18bed7128544a50f75897db9974436ea9bfba560420b646af27a9a9b35211"},
"text_diff": {:hex, :text_diff, "0.1.0", "1caf3175e11a53a9a139bc9339bd607c47b9e376b073d4571c031913317fecaa", [:mix], [], "hexpm", "d1ffaaecab338e49357b6daa82e435f877e0649041ace7755583a0ea3362dbd7"}, "text_diff": {:hex, :text_diff, "0.1.0", "1caf3175e11a53a9a139bc9339bd607c47b9e376b073d4571c031913317fecaa", [:mix], [], "hexpm", "d1ffaaecab338e49357b6daa82e435f877e0649041ace7755583a0ea3362dbd7"},
"thousand_island": {:hex, :thousand_island, "1.4.1", "8df065e627407e281f7935da5ad0f3842d10eb721afa92e760b720d71e2e37aa", [], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "204a8640e5d2818589b87286ae66160978628d7edf6095181cbe0440765fb6c1"}, "thousand_island": {:hex, :thousand_island, "1.4.1", "8df065e627407e281f7935da5ad0f3842d10eb721afa92e760b720d71e2e37aa", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "204a8640e5d2818589b87286ae66160978628d7edf6095181cbe0440765fb6c1"},
"tidewave": {:hex, :tidewave, "0.5.0", "8f278d7eb2d0af36ae6d4f73a5872bd066815bd57b57401125187ba901f095a4", [:mix], [{:circular_buffer, "~> 0.4 or ~> 1.0", [hex: :circular_buffer, repo: "hexpm", optional: false]}, {:igniter, "~> 0.6", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_live_reload, ">= 1.6.1", [hex: :phoenix_live_reload, repo: "hexpm", optional: true]}, {:plug, "~> 1.17", [hex: :plug, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "9a1eb5d2f12ff4912328dfbfe652c27fded462c6ed6fd11814ee28d3e9d016b4"}, "tidewave": {:hex, :tidewave, "0.5.0", "8f278d7eb2d0af36ae6d4f73a5872bd066815bd57b57401125187ba901f095a4", [:mix], [{:circular_buffer, "~> 0.4 or ~> 1.0", [hex: :circular_buffer, repo: "hexpm", optional: false]}, {:igniter, "~> 0.6", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_live_reload, ">= 1.6.1", [hex: :phoenix_live_reload, repo: "hexpm", optional: true]}, {:plug, "~> 1.17", [hex: :plug, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "9a1eb5d2f12ff4912328dfbfe652c27fded462c6ed6fd11814ee28d3e9d016b4"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"},
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},

View file

@ -16,7 +16,7 @@ msgid "Actions"
msgstr "Aktionen" msgstr "Aktionen"
#: lib/mv_web/live/member_live/index.html.heex:200 #: lib/mv_web/live/member_live/index.html.heex:200
#: lib/mv_web/live/user_live/index.html.heex:65 #: lib/mv_web/live/user_live/index.html.heex:72
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure?" msgid "Are you sure?"
msgstr "Bist du sicher?" msgstr "Bist du sicher?"
@ -35,14 +35,14 @@ msgid "City"
msgstr "Stadt" msgstr "Stadt"
#: lib/mv_web/live/member_live/index.html.heex:202 #: lib/mv_web/live/member_live/index.html.heex:202
#: lib/mv_web/live/user_live/index.html.heex:67 #: lib/mv_web/live/user_live/index.html.heex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete" msgid "Delete"
msgstr "Löschen" msgstr "Löschen"
#: lib/mv_web/live/member_live/index.html.heex:194 #: lib/mv_web/live/member_live/index.html.heex:194
#: lib/mv_web/live/user_live/form.ex:141 #: lib/mv_web/live/user_live/form.ex:251
#: lib/mv_web/live/user_live/index.html.heex:59 #: lib/mv_web/live/user_live/index.html.heex:66
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit" msgid "Edit"
msgstr "Bearbeite" msgstr "Bearbeite"
@ -88,7 +88,7 @@ msgid "New Member"
msgstr "Neues Mitglied" msgstr "Neues Mitglied"
#: lib/mv_web/live/member_live/index.html.heex:191 #: lib/mv_web/live/member_live/index.html.heex:191
#: lib/mv_web/live/user_live/index.html.heex:56 #: lib/mv_web/live/user_live/index.html.heex:63
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Show" msgid "Show"
msgstr "Anzeigen" msgstr "Anzeigen"
@ -161,7 +161,7 @@ msgstr "Mitglied speichern"
#: lib/mv_web/live/custom_field_live/form.ex:64 #: lib/mv_web/live/custom_field_live/form.ex:64
#: lib/mv_web/live/custom_field_value_live/form.ex:74 #: lib/mv_web/live/custom_field_value_live/form.ex:74
#: lib/mv_web/live/member_live/form.ex:79 #: lib/mv_web/live/member_live/form.ex:79
#: lib/mv_web/live/user_live/form.ex:124 #: lib/mv_web/live/user_live/form.ex:234
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Saving..." msgid "Saving..."
msgstr "Speichern..." msgstr "Speichern..."
@ -255,7 +255,7 @@ msgstr "Ihr Passwort wurde erfolgreich zurückgesetzt"
#: lib/mv_web/live/custom_field_live/form.ex:67 #: lib/mv_web/live/custom_field_live/form.ex:67
#: lib/mv_web/live/custom_field_value_live/form.ex:77 #: lib/mv_web/live/custom_field_value_live/form.ex:77
#: lib/mv_web/live/member_live/form.ex:82 #: lib/mv_web/live/member_live/form.ex:82
#: lib/mv_web/live/user_live/form.ex:127 #: lib/mv_web/live/user_live/form.ex:237
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Cancel" msgid "Cancel"
msgstr "Abbrechen" msgstr "Abbrechen"
@ -335,6 +335,7 @@ msgstr "Nicht gesetzt"
#: lib/mv_web/live/user_live/form.ex:107 #: lib/mv_web/live/user_live/form.ex:107
#: lib/mv_web/live/user_live/form.ex:115 #: lib/mv_web/live/user_live/form.ex:115
#: lib/mv_web/live/user_live/form.ex:210
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Note" msgid "Note"
msgstr "Hinweis" msgstr "Hinweis"
@ -375,7 +376,7 @@ msgstr "Mitglied auswählen"
msgid "Settings" msgid "Settings"
msgstr "Einstellungen" msgstr "Einstellungen"
#: lib/mv_web/live/user_live/form.ex:125 #: lib/mv_web/live/user_live/form.ex:235
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Save User" msgid "Save User"
msgstr "Benutzer*in speichern" msgstr "Benutzer*in speichern"
@ -400,7 +401,7 @@ msgstr "Nicht unterstützter Wertetyp: %{type}"
msgid "Use this form to manage user records in your database." msgid "Use this form to manage user records in your database."
msgstr "Verwenden Sie dieses Formular, um Benutzer*innen-Datensätze zu verwalten." msgstr "Verwenden Sie dieses Formular, um Benutzer*innen-Datensätze zu verwalten."
#: lib/mv_web/live/user_live/form.ex:142 #: lib/mv_web/live/user_live/form.ex:252
#: lib/mv_web/live/user_live/show.ex:34 #: lib/mv_web/live/user_live/show.ex:34
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "User" msgid "User"
@ -428,7 +429,7 @@ msgstr "aufsteigend"
msgid "descending" msgid "descending"
msgstr "absteigend" msgstr "absteigend"
#: lib/mv_web/live/user_live/form.ex:141 #: lib/mv_web/live/user_live/form.ex:251
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "New" msgid "New"
msgstr "Neue*r" msgstr "Neue*r"
@ -503,6 +504,8 @@ msgstr "Passwort setzen"
msgid "User will be created without a password. Check 'Set Password' to add one." msgid "User will be created without a password. Check 'Set Password' to add one."
msgstr "Benutzer*in wird ohne Passwort erstellt. Aktivieren Sie 'Passwort setzen', um eines hinzuzufügen." msgstr "Benutzer*in wird ohne Passwort erstellt. Aktivieren Sie 'Passwort setzen', um eines hinzuzufügen."
#: lib/mv_web/live/user_live/form.ex:126
#: lib/mv_web/live/user_live/index.html.heex:53
#: lib/mv_web/live/user_live/show.ex:55 #: lib/mv_web/live/user_live/show.ex:55
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Linked Member" msgid "Linked Member"
@ -513,6 +516,7 @@ msgstr "Verknüpftes Mitglied"
msgid "Linked User" msgid "Linked User"
msgstr "Verknüpfte*r Benutzer*in" msgstr "Verknüpfte*r Benutzer*in"
#: lib/mv_web/live/user_live/index.html.heex:57
#: lib/mv_web/live/user_live/show.ex:65 #: lib/mv_web/live/user_live/show.ex:65
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No member linked" msgid "No member linked"
@ -659,4 +663,54 @@ msgstr "Verwende dieses Formular, um Benutzerdefinierte Feldwerte in deiner Date
#: lib/mv_web/live/custom_field_live/show.ex:56 #: lib/mv_web/live/custom_field_live/show.ex:56
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Auto-generated identifier (immutable)" msgid "Auto-generated identifier (immutable)"
msgstr "Automatisch generierter Identifier" msgstr "Automatisch generierte Kennung (unveränderlich)"
#: lib/mv_web/live/user_live/form.ex:210
#, elixir-autogen, elixir-format
msgid "A member with this email already exists. To link with a different member, please change one of the email addresses first."
msgstr "Ein Mitglied mit dieser E-Mail-Adresse existiert bereits. Um mit einem anderen Mitglied zu verknüpfen, ändern Sie bitte zuerst eine der E-Mail-Adressen."
#: lib/mv_web/live/user_live/form.ex:185
#, elixir-autogen, elixir-format
msgid "Available members"
msgstr "Verfügbare Mitglieder"
#: lib/mv_web/live/user_live/form.ex:152
#, elixir-autogen, elixir-format
msgid "Member will be unlinked when you save. Cannot select new member until saved."
msgstr "Mitglied wird beim Speichern entverknüpft. Neues Mitglied kann erst nach dem Speichern ausgewählt werden."
#: lib/mv_web/live/user_live/form.ex:226
#, elixir-autogen, elixir-format
msgid "Save to confirm linking."
msgstr "Speichern, um die Verknüpfung zu bestätigen."
#: lib/mv_web/live/user_live/form.ex:169
#, elixir-autogen, elixir-format
msgid "Search for a member to link..."
msgstr "Nach einem Mitglied zum Verknüpfen suchen..."
#: lib/mv_web/live/user_live/form.ex:173
#, elixir-autogen, elixir-format
msgid "Search for member to link"
msgstr "Nach Mitglied zum Verknüpfen suchen"
#: lib/mv_web/live/user_live/form.ex:223
#, elixir-autogen, elixir-format
msgid "Selected"
msgstr "Ausgewählt"
#: lib/mv_web/live/user_live/form.ex:143
#, elixir-autogen, elixir-format
msgid "Unlink Member"
msgstr "Mitglied entverknüpfen"
#: lib/mv_web/live/user_live/form.ex:152
#, elixir-autogen, elixir-format
msgid "Unlinking scheduled"
msgstr "Entverknüpfung geplant"
#~ #: lib/mv_web/live/custom_field_live/form.ex:58
#~ #, elixir-autogen, elixir-format
#~ msgid "Slug"
#~ msgstr "Slug"

View file

@ -17,7 +17,7 @@ msgid "Actions"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:200 #: lib/mv_web/live/member_live/index.html.heex:200
#: lib/mv_web/live/user_live/index.html.heex:65 #: lib/mv_web/live/user_live/index.html.heex:72
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure?" msgid "Are you sure?"
msgstr "" msgstr ""
@ -36,14 +36,14 @@ msgid "City"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:202 #: lib/mv_web/live/member_live/index.html.heex:202
#: lib/mv_web/live/user_live/index.html.heex:67 #: lib/mv_web/live/user_live/index.html.heex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete" msgid "Delete"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:194 #: lib/mv_web/live/member_live/index.html.heex:194
#: lib/mv_web/live/user_live/form.ex:141 #: lib/mv_web/live/user_live/form.ex:251
#: lib/mv_web/live/user_live/index.html.heex:59 #: lib/mv_web/live/user_live/index.html.heex:66
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit" msgid "Edit"
msgstr "" msgstr ""
@ -89,7 +89,7 @@ msgid "New Member"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:191 #: lib/mv_web/live/member_live/index.html.heex:191
#: lib/mv_web/live/user_live/index.html.heex:56 #: lib/mv_web/live/user_live/index.html.heex:63
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Show" msgid "Show"
msgstr "" msgstr ""
@ -162,7 +162,7 @@ msgstr ""
#: lib/mv_web/live/custom_field_live/form.ex:64 #: lib/mv_web/live/custom_field_live/form.ex:64
#: lib/mv_web/live/custom_field_value_live/form.ex:74 #: lib/mv_web/live/custom_field_value_live/form.ex:74
#: lib/mv_web/live/member_live/form.ex:79 #: lib/mv_web/live/member_live/form.ex:79
#: lib/mv_web/live/user_live/form.ex:124 #: lib/mv_web/live/user_live/form.ex:234
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Saving..." msgid "Saving..."
msgstr "" msgstr ""
@ -256,7 +256,7 @@ msgstr ""
#: lib/mv_web/live/custom_field_live/form.ex:67 #: lib/mv_web/live/custom_field_live/form.ex:67
#: lib/mv_web/live/custom_field_value_live/form.ex:77 #: lib/mv_web/live/custom_field_value_live/form.ex:77
#: lib/mv_web/live/member_live/form.ex:82 #: lib/mv_web/live/member_live/form.ex:82
#: lib/mv_web/live/user_live/form.ex:127 #: lib/mv_web/live/user_live/form.ex:237
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
@ -336,6 +336,7 @@ msgstr ""
#: lib/mv_web/live/user_live/form.ex:107 #: lib/mv_web/live/user_live/form.ex:107
#: lib/mv_web/live/user_live/form.ex:115 #: lib/mv_web/live/user_live/form.ex:115
#: lib/mv_web/live/user_live/form.ex:210
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Note" msgid "Note"
msgstr "" msgstr ""
@ -376,7 +377,7 @@ msgstr ""
msgid "Settings" msgid "Settings"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:125 #: lib/mv_web/live/user_live/form.ex:235
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Save User" msgid "Save User"
msgstr "" msgstr ""
@ -401,7 +402,7 @@ msgstr ""
msgid "Use this form to manage user records in your database." msgid "Use this form to manage user records in your database."
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:142 #: lib/mv_web/live/user_live/form.ex:252
#: lib/mv_web/live/user_live/show.ex:34 #: lib/mv_web/live/user_live/show.ex:34
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "User" msgid "User"
@ -429,7 +430,7 @@ msgstr ""
msgid "descending" msgid "descending"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:141 #: lib/mv_web/live/user_live/form.ex:251
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "New" msgid "New"
msgstr "" msgstr ""
@ -504,6 +505,8 @@ msgstr ""
msgid "User will be created without a password. Check 'Set Password' to add one." msgid "User will be created without a password. Check 'Set Password' to add one."
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:126
#: lib/mv_web/live/user_live/index.html.heex:53
#: lib/mv_web/live/user_live/show.ex:55 #: lib/mv_web/live/user_live/show.ex:55
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Linked Member" msgid "Linked Member"
@ -514,6 +517,7 @@ msgstr ""
msgid "Linked User" msgid "Linked User"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/index.html.heex:57
#: lib/mv_web/live/user_live/show.ex:65 #: lib/mv_web/live/user_live/show.ex:65
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No member linked" msgid "No member linked"
@ -661,3 +665,48 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Auto-generated identifier (immutable)" msgid "Auto-generated identifier (immutable)"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:210
#, elixir-autogen, elixir-format
msgid "A member with this email already exists. To link with a different member, please change one of the email addresses first."
msgstr ""
#: lib/mv_web/live/user_live/form.ex:185
#, elixir-autogen, elixir-format
msgid "Available members"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:152
#, elixir-autogen, elixir-format
msgid "Member will be unlinked when you save. Cannot select new member until saved."
msgstr ""
#: lib/mv_web/live/user_live/form.ex:226
#, elixir-autogen, elixir-format
msgid "Save to confirm linking."
msgstr ""
#: lib/mv_web/live/user_live/form.ex:169
#, elixir-autogen, elixir-format
msgid "Search for a member to link..."
msgstr ""
#: lib/mv_web/live/user_live/form.ex:173
#, elixir-autogen, elixir-format
msgid "Search for member to link"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:223
#, elixir-autogen, elixir-format
msgid "Selected"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:143
#, elixir-autogen, elixir-format
msgid "Unlink Member"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:152
#, elixir-autogen, elixir-format
msgid "Unlinking scheduled"
msgstr ""

View file

@ -17,7 +17,7 @@ msgid "Actions"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:200 #: lib/mv_web/live/member_live/index.html.heex:200
#: lib/mv_web/live/user_live/index.html.heex:65 #: lib/mv_web/live/user_live/index.html.heex:72
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Are you sure?" msgid "Are you sure?"
msgstr "" msgstr ""
@ -36,14 +36,14 @@ msgid "City"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:202 #: lib/mv_web/live/member_live/index.html.heex:202
#: lib/mv_web/live/user_live/index.html.heex:67 #: lib/mv_web/live/user_live/index.html.heex:74
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Delete" msgid "Delete"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:194 #: lib/mv_web/live/member_live/index.html.heex:194
#: lib/mv_web/live/user_live/form.ex:141 #: lib/mv_web/live/user_live/form.ex:251
#: lib/mv_web/live/user_live/index.html.heex:59 #: lib/mv_web/live/user_live/index.html.heex:66
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Edit" msgid "Edit"
msgstr "" msgstr ""
@ -89,7 +89,7 @@ msgid "New Member"
msgstr "" msgstr ""
#: lib/mv_web/live/member_live/index.html.heex:191 #: lib/mv_web/live/member_live/index.html.heex:191
#: lib/mv_web/live/user_live/index.html.heex:56 #: lib/mv_web/live/user_live/index.html.heex:63
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Show" msgid "Show"
msgstr "" msgstr ""
@ -162,7 +162,7 @@ msgstr ""
#: lib/mv_web/live/custom_field_live/form.ex:64 #: lib/mv_web/live/custom_field_live/form.ex:64
#: lib/mv_web/live/custom_field_value_live/form.ex:74 #: lib/mv_web/live/custom_field_value_live/form.ex:74
#: lib/mv_web/live/member_live/form.ex:79 #: lib/mv_web/live/member_live/form.ex:79
#: lib/mv_web/live/user_live/form.ex:124 #: lib/mv_web/live/user_live/form.ex:234
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Saving..." msgid "Saving..."
msgstr "" msgstr ""
@ -256,7 +256,7 @@ msgstr ""
#: lib/mv_web/live/custom_field_live/form.ex:67 #: lib/mv_web/live/custom_field_live/form.ex:67
#: lib/mv_web/live/custom_field_value_live/form.ex:77 #: lib/mv_web/live/custom_field_value_live/form.ex:77
#: lib/mv_web/live/member_live/form.ex:82 #: lib/mv_web/live/member_live/form.ex:82
#: lib/mv_web/live/user_live/form.ex:127 #: lib/mv_web/live/user_live/form.ex:237
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
@ -336,6 +336,7 @@ msgstr ""
#: lib/mv_web/live/user_live/form.ex:107 #: lib/mv_web/live/user_live/form.ex:107
#: lib/mv_web/live/user_live/form.ex:115 #: lib/mv_web/live/user_live/form.ex:115
#: lib/mv_web/live/user_live/form.ex:210
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Note" msgid "Note"
msgstr "" msgstr ""
@ -376,7 +377,7 @@ msgstr ""
msgid "Settings" msgid "Settings"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:125 #: lib/mv_web/live/user_live/form.ex:235
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Save User" msgid "Save User"
msgstr "" msgstr ""
@ -401,7 +402,7 @@ msgstr ""
msgid "Use this form to manage user records in your database." msgid "Use this form to manage user records in your database."
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:142 #: lib/mv_web/live/user_live/form.ex:252
#: lib/mv_web/live/user_live/show.ex:34 #: lib/mv_web/live/user_live/show.ex:34
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "User" msgid "User"
@ -429,7 +430,7 @@ msgstr ""
msgid "descending" msgid "descending"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:141 #: lib/mv_web/live/user_live/form.ex:251
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "New" msgid "New"
msgstr "" msgstr ""
@ -504,6 +505,8 @@ msgstr "Set Password"
msgid "User will be created without a password. Check 'Set Password' to add one." msgid "User will be created without a password. Check 'Set Password' to add one."
msgstr "User will be created without a password. Check 'Set Password' to add one." msgstr "User will be created without a password. Check 'Set Password' to add one."
#: lib/mv_web/live/user_live/form.ex:126
#: lib/mv_web/live/user_live/index.html.heex:53
#: lib/mv_web/live/user_live/show.ex:55 #: lib/mv_web/live/user_live/show.ex:55
#, elixir-autogen, elixir-format, fuzzy #, elixir-autogen, elixir-format, fuzzy
msgid "Linked Member" msgid "Linked Member"
@ -514,6 +517,7 @@ msgstr ""
msgid "Linked User" msgid "Linked User"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/index.html.heex:57
#: lib/mv_web/live/user_live/show.ex:65 #: lib/mv_web/live/user_live/show.ex:65
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "No member linked" msgid "No member linked"
@ -661,3 +665,53 @@ msgstr ""
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Auto-generated identifier (immutable)" msgid "Auto-generated identifier (immutable)"
msgstr "" msgstr ""
#: lib/mv_web/live/user_live/form.ex:210
#, elixir-autogen, elixir-format
msgid "A member with this email already exists. To link with a different member, please change one of the email addresses first."
msgstr ""
#: lib/mv_web/live/user_live/form.ex:185
#, elixir-autogen, elixir-format
msgid "Available members"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:152
#, elixir-autogen, elixir-format
msgid "Member will be unlinked when you save. Cannot select new member until saved."
msgstr ""
#: lib/mv_web/live/user_live/form.ex:226
#, elixir-autogen, elixir-format
msgid "Save to confirm linking."
msgstr ""
#: lib/mv_web/live/user_live/form.ex:169
#, elixir-autogen, elixir-format
msgid "Search for a member to link..."
msgstr ""
#: lib/mv_web/live/user_live/form.ex:173
#, elixir-autogen, elixir-format
msgid "Search for member to link"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:223
#, elixir-autogen, elixir-format, fuzzy
msgid "Selected"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:143
#, elixir-autogen, elixir-format
msgid "Unlink Member"
msgstr ""
#: lib/mv_web/live/user_live/form.ex:152
#, elixir-autogen, elixir-format
msgid "Unlinking scheduled"
msgstr ""
#~ #: lib/mv_web/live/custom_field_live/form.ex:58
#~ #, elixir-autogen, elixir-format
#~ msgid "Slug"
#~ msgstr ""