From 61dc095498eb92f42a163693ba7506ba1f489975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Eppl=C3=A9e?= Date: Wed, 28 May 2025 17:08:02 +0200 Subject: [PATCH 01/85] wip: feat(ci): Build docker container --- .drone.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.drone.yml b/.drone.yml index 3195217..569ce6a 100644 --- a/.drone.yml +++ b/.drone.yml @@ -8,6 +8,12 @@ services: environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres + - name: docker + image: docker:dind + privileged: true + volumes: + - name: dockersock + path: /var/run trigger: event: @@ -95,11 +101,25 @@ steps: - name: cache path: /cache + - name: build & publish container? + image: docker:dind + volumes: + - name: dockersock + path: /var/run + commands: + - sleep 6 # give docker time to start + - docker build --tag git.local-it.org/ci-builder/mitgliederverwaltung:latest . + - docker login --username $DRONE_FORGEJO_ACCOUNT_USERNAME --password $DRONE_FORGEJO_ACCOUNT_PASSWORD git.local-it.org + - docker push git.local-it.org/ci-builder/mitgliederverwaltung:latest + volumes: - name: cache host: path: /tmp/drone_cache + - name: dockersock + temp: {} + --- kind: pipeline type: docker From 92588cbef2f541a19c706aaf0fae2e0918514a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Eppl=C3=A9e?= Date: Thu, 17 Jul 2025 15:23:39 +0200 Subject: [PATCH 02/85] dropme: remove other drone tasks for faster debugging --- .drone.yml | 81 ------------------------------------------------------ 1 file changed, 81 deletions(-) diff --git a/.drone.yml b/.drone.yml index 569ce6a..7f0dead 100644 --- a/.drone.yml +++ b/.drone.yml @@ -20,87 +20,6 @@ trigger: - push steps: - - name: compute cache key - image: docker.io/library/elixir:1.18.3-otp-27 - commands: - - mix_lock_hash=$(sha256sum mix.lock | cut -d ' ' -f 1) - - echo "$DRONE_REPO_OWNER/$DRONE_REPO_NAME/$mix_lock_hash" >> .cache_key - # Print cache key for debugging - - cat .cache_key - - - name: restore-cache - image: drillster/drone-volume-cache - settings: - restore: true - mount: - - ./deps - - ./_build - ttl: 30 - volumes: - - name: cache - path: /cache - - - name: lint - image: docker.io/library/elixir:1.18.3-otp-27 - commands: - # Install hex package manager - - mix local.hex --force - # Fetch dependencies - - mix deps.get - # Check for compilation errors & warnings - - mix compile --warnings-as-errors - # Check formatting - - mix format --check-formatted - # Security checks - - mix sobelow --config - # Check dependencies for known vulnerabilities - - mix deps.audit - # Check for dependencies that are not maintained anymore - - mix hex.audit - # Provide hints for improving code quality - - mix credo - - - name: wait_for_postgres - image: docker.io/library/postgres:17.5 - commands: - # Wait for postgres to become available - - | - for i in {1..20}; do - if pg_isready -h postgres -U postgres; then - exit 0 - else - true - fi - sleep 2 - done - echo "Postgres did not become available, aborting." - exit 1 - - - name: test - image: docker.io/library/elixir:1.18.3-otp-27 - environment: - MIX_ENV: test - TEST_POSTGRES_HOST: postgres - TEST_POSTGRES_PORT: 5432 - commands: - # Install hex package manager - - mix local.hex --force - # Fetch dependencies - - mix deps.get - # Run tests - - mix test - - - name: rebuild-cache - image: drillster/drone-volume-cache - settings: - rebuild: true - mount: - - ./deps - - ./_build - volumes: - - name: cache - path: /cache - - name: build & publish container? image: docker:dind volumes: From ddf9348eb865c4b9f9a03c0f35f643e263391407 Mon Sep 17 00:00:00 2001 From: carla Date: Mon, 4 Aug 2025 15:02:54 +0200 Subject: [PATCH 03/85] feat: add overrides for sign in page --- assets/css/app.css | 2 +- assets/tailwind.config.js | 1 + lib/mv_web/auth_overrides.ex | 11 ++++++++--- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/assets/css/app.css b/assets/css/app.css index 0417463..ea63a2d 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -1,7 +1,7 @@ /* See the Tailwind configuration guide for advanced usage https://tailwindcss.com/docs/configuration */ -@import "tailwindcss" source(none); +@import "tailwindcss"; @source "../css"; @source "../js"; @source "../../lib/mv_web"; diff --git a/assets/tailwind.config.js b/assets/tailwind.config.js index 873d6d6..d87d46f 100644 --- a/assets/tailwind.config.js +++ b/assets/tailwind.config.js @@ -12,6 +12,7 @@ module.exports = { "../lib/mv_web.ex", "../lib/mv_web/**/*.*ex" ], + safelist: ['h-screen', 'px-6', 'h-screen', 'place-items-center', 'px-20', 'px-24'], // Classes that are used by AshAuthentication Sign-In Page are otherwise purged theme: { extend: { colors: { diff --git a/lib/mv_web/auth_overrides.ex b/lib/mv_web/auth_overrides.ex index bec3354..fab8653 100644 --- a/lib/mv_web/auth_overrides.ex +++ b/lib/mv_web/auth_overrides.ex @@ -14,7 +14,12 @@ defmodule MvWeb.AuthOverrides do # set :text_class, "bg-red-500" # end - # override AshAuthentication.Phoenix.Components.SignIn do - # set :show_banner, false - # end + override AshAuthentication.Phoenix.Components.SignIn do + set :root_class, "min-w-md" + end + + override AshAuthentication.Phoenix.Components.Banner do + set :text, "Mitgliederverwaltung" + set :image_url, nil + end end From 2a6f1ccf80e038501974adb44f33d94222643628 Mon Sep 17 00:00:00 2001 From: carla Date: Wed, 6 Aug 2025 12:42:17 +0200 Subject: [PATCH 04/85] feat: add translation --- Justfile | 2 +- lib/mv_web/auth_overrides.ex | 19 ++++-- lib/mv_web/components/layouts/root.html.heex | 2 +- lib/mv_web/live_helpers.ex | 2 +- lib/mv_web/live_user_auth.ex | 3 + lib/mv_web/router.ex | 14 ++--- priv/gettext/auth.pot | 64 +++++++++++++++++++ priv/gettext/de/LC_MESSAGES/auth.po | 66 ++++++++++++++++++++ priv/gettext/de/LC_MESSAGES/default.po | 21 ++++--- priv/gettext/default.pot | 21 ++++--- priv/gettext/en/LC_MESSAGES/auth.po | 63 +++++++++++++++++++ priv/gettext/en/LC_MESSAGES/default.po | 21 ++++--- 12 files changed, 260 insertions(+), 38 deletions(-) create mode 100644 priv/gettext/auth.pot create mode 100644 priv/gettext/de/LC_MESSAGES/auth.po create mode 100644 priv/gettext/en/LC_MESSAGES/auth.po diff --git a/Justfile b/Justfile index 21e54fb..1874b67 100644 --- a/Justfile +++ b/Justfile @@ -23,7 +23,7 @@ ci-dev: lint audit test gettext: mix gettext.extract - mix gettext.merge priv/gettext + mix gettext.merge priv/gettext --on-obsolete=mark_as_obsolete lint: mix format --check-formatted diff --git a/lib/mv_web/auth_overrides.ex b/lib/mv_web/auth_overrides.ex index fab8653..367982e 100644 --- a/lib/mv_web/auth_overrides.ex +++ b/lib/mv_web/auth_overrides.ex @@ -1,5 +1,6 @@ defmodule MvWeb.AuthOverrides do use AshAuthentication.Phoenix.Overrides + use Gettext, backend: MvWeb.Gettext # configure your UI overrides here @@ -14,12 +15,22 @@ defmodule MvWeb.AuthOverrides do # set :text_class, "bg-red-500" # end - override AshAuthentication.Phoenix.Components.SignIn do + # Avoid full-width for the Sign In Form + override AshAuthentication.Phoenix.Components.SignIn do set :root_class, "min-w-md" - end + end - override AshAuthentication.Phoenix.Components.Banner do + # Replace banner logo with text + override AshAuthentication.Phoenix.Components.Banner do set :text, "Mitgliederverwaltung" set :image_url, nil - end + end + + # Translate the or in the horizontal rule to German + override AshAuthentication.Phoenix.Components.HorizontalRule do + set :text, + Gettext.with_locale(MvWeb.Gettext, "de", fn -> + Gettext.gettext(MvWeb.Gettext, "or") + end) + end end diff --git a/lib/mv_web/components/layouts/root.html.heex b/lib/mv_web/components/layouts/root.html.heex index 5ee0fef..e1b639d 100644 --- a/lib/mv_web/components/layouts/root.html.heex +++ b/lib/mv_web/components/layouts/root.html.heex @@ -1,5 +1,5 @@ - + {Application.get_env(:live_debugger, :live_debugger_tags)} diff --git a/lib/mv_web/live_helpers.ex b/lib/mv_web/live_helpers.ex index 03d7d45..331bb5c 100644 --- a/lib/mv_web/live_helpers.ex +++ b/lib/mv_web/live_helpers.ex @@ -1,6 +1,6 @@ defmodule MvWeb.LiveHelpers do def on_mount(:default, _params, session, socket) do - locale = session["locale"] || "en" + locale = session["locale"] || "de" Gettext.put_locale(locale) {:cont, socket} end diff --git a/lib/mv_web/live_user_auth.ex b/lib/mv_web/live_user_auth.ex index 67bef70..d211245 100644 --- a/lib/mv_web/live_user_auth.ex +++ b/lib/mv_web/live_user_auth.ex @@ -37,6 +37,9 @@ defmodule MvWeb.LiveUserAuth do end def on_mount(:live_no_user, _params, _session, socket) do + Gettext.put_locale(MvWeb.Gettext, "de") + {:cont, assign(socket, :locale, "de")} + if socket.assigns[:current_user] do {:halt, Phoenix.LiveView.redirect(socket, to: ~p"/")} else diff --git a/lib/mv_web/router.ex b/lib/mv_web/router.ex index 696a491..82e9f00 100644 --- a/lib/mv_web/router.ex +++ b/lib/mv_web/router.ex @@ -85,19 +85,19 @@ defmodule MvWeb.Router do reset_path: "/reset", auth_routes_prefix: "/auth", on_mount: [{MvWeb.LiveUserAuth, :live_no_user}], - overrides: [MvWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.Default], - gettext_backend: {MvWeb.Gettext, "default"} + overrides: [MvWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.DaisyUI], + gettext_backend: {MvWeb.Gettext, "auth"} # Remove this if you do not want to use the reset password feature reset_route auth_routes_prefix: "/auth", - overrides: [MvWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.Default], - gettext_backend: {MvWeb.Gettext, "default"} + overrides: [MvWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.DaisyUI], + gettext_backend: {MvWeb.Gettext, "auth"} # Remove this if you do not use the confirmation strategy confirm_route Mv.Accounts.User, :confirm_new_user, auth_routes_prefix: "/auth", - overrides: [MvWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.Default], - gettext_backend: {MvWeb.Gettext, "default"} + overrides: [MvWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.DaisyUI], + gettext_backend: {MvWeb.Gettext, "auth"} # Remove this if you do not use the magic link strategy. # magic_sign_in_route(Mv.Accounts.User, :magic_link, @@ -139,7 +139,7 @@ defmodule MvWeb.Router do end defp set_locale(conn, _opts) do - locale = get_session(conn, :locale) || "en" + locale = get_session(conn, :locale) || "de" Gettext.put_locale(MvWeb.Gettext, locale) conn end diff --git a/priv/gettext/auth.pot b/priv/gettext/auth.pot new file mode 100644 index 0000000..29ee991 --- /dev/null +++ b/priv/gettext/auth.pot @@ -0,0 +1,64 @@ +## This file is a PO Template file. +## +## "msgid"s here are often extracted from source code. +## Add new messages manually only if they're dynamic +## messages that can't be statically extracted. +## +## Leave "msgstr"s empty as changing them here has no +## effect: edit them in PO (.po) files instead. +# +msgid "" +msgstr "" +"Language: en\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Already have an account?" +msgstr "" + +msgid "Email or password was incorrect" +msgstr "" + +msgid "Email" +msgstr "" + +msgid "Forgot your password?" +msgstr "" + +msgid "If this user exists in our database you will contacted with a sign-in link shortly." +msgstr "" + +msgid "If this user exists in our system, you will be contacted with reset instructions shortly." +msgstr "" + +msgid "Need an account?" +msgstr "" + +msgid "Password" +msgstr "" + +msgid "Password Confirmation" +msgstr "" + +msgid "Request magic link" +msgstr "" + +msgid "Request password reset token" +msgstr "" + +msgid "Requesting ..." +msgstr "" + +msgid "Reset password with token" +msgstr "" + +msgid "Sign in" +msgstr "" + +msgid "Signing in ..." +msgstr "" + +msgid "Your password has successfully been reset" +msgstr "" diff --git a/priv/gettext/de/LC_MESSAGES/auth.po b/priv/gettext/de/LC_MESSAGES/auth.po new file mode 100644 index 0000000..0f2202d --- /dev/null +++ b/priv/gettext/de/LC_MESSAGES/auth.po @@ -0,0 +1,66 @@ +## "msgid"s in this file come from POT (.pot) files. +### +### Do not add, change, or remove "msgid"s manually here as +### they're tied to the ones in the corresponding POT file +### (with the same domain). +### +### Use "mix gettext.extract --merge" or "mix gettext.merge" +### to merge POT files into PO files. +msgid "" +msgstr "" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Already have an account?" +msgstr "Bereit zum Anmelden?" + +msgid "Email or password was incorrect" +msgstr "Email oder Passwort nicht korrekt" + +msgid "Email" +msgstr "Email" + +msgid "Forgot your password?" +msgstr "Passwort vergessen?" + +msgid "If this user exists in our database you will contacted with a sign-in link shortly." +msgstr "Falls dieser Benutzer bekannt ist, wird jetzt eine Email mit Anmelde-Link versendet." + +msgid "If this user exists in our system, you will be contacted with reset instructions shortly." +msgstr "Falls dieser Benutzer bekannt ist, wird jetzt eine Email mit einer Anleitung zum Zurücksetzen versendet." + +msgid "Need an account?" +msgstr "Konto anlegen?" + +msgid "Password" +msgstr "Passwort" + +msgid "Password Confirmation" +msgstr "Passwort Wiederholung" + +msgid "Request magic link" +msgstr "Magischen Link anfordern" + +msgid "Request password reset token" +msgstr "Passwort zurücksetzen" + +msgid "Requesting ..." +msgstr "Anfrage låuft..." + +msgid "Reset password with token" +msgstr "Neues Passwort setzen" + +msgid "Sign in" +msgstr "Anmelden" + +msgid "Signing in ..." +msgstr "Anmelden..." + +msgid "Your password has successfully been reset" +msgstr "Das Passwort wurde erfolgreich zurückgesetzt" + +msgid "Sign in with Rauthy" +msgstr "Anmelden mit der Vereinscloud" diff --git a/priv/gettext/de/LC_MESSAGES/default.po b/priv/gettext/de/LC_MESSAGES/default.po index 45723e7..1a7cf7e 100644 --- a/priv/gettext/de/LC_MESSAGES/default.po +++ b/priv/gettext/de/LC_MESSAGES/default.po @@ -16,7 +16,7 @@ msgid "Actions" msgstr "Aktionen" #: lib/mv_web/live/member_live/index.html.heex:77 -#: lib/mv_web/live/user_live/index.html.heex:69 +#: lib/mv_web/live/user_live/index.html.heex:65 #, elixir-autogen, elixir-format msgid "Are you sure?" msgstr "Bist du sicher?" @@ -35,14 +35,14 @@ msgid "City" msgstr "Stadt" #: lib/mv_web/live/member_live/index.html.heex:79 -#: lib/mv_web/live/user_live/index.html.heex:71 +#: lib/mv_web/live/user_live/index.html.heex:67 #, elixir-autogen, elixir-format msgid "Delete" msgstr "Löschen" #: lib/mv_web/live/member_live/index.html.heex:71 #: lib/mv_web/live/user_live/form.ex:109 -#: lib/mv_web/live/user_live/index.html.heex:63 +#: lib/mv_web/live/user_live/index.html.heex:59 #, elixir-autogen, elixir-format msgid "Edit" msgstr "Bearbeite" @@ -57,7 +57,7 @@ msgstr "Mitglied bearbeiten" #: lib/mv_web/live/member_live/index.html.heex:58 #: lib/mv_web/live/member_live/show.ex:27 #: lib/mv_web/live/user_live/form.ex:14 -#: lib/mv_web/live/user_live/index.html.heex:48 +#: lib/mv_web/live/user_live/index.html.heex:44 #: lib/mv_web/live/user_live/show.ex:24 #, elixir-autogen, elixir-format msgid "Email" @@ -88,7 +88,7 @@ msgid "New Member" msgstr "Neues Mitglied" #: lib/mv_web/live/member_live/index.html.heex:68 -#: lib/mv_web/live/user_live/index.html.heex:60 +#: lib/mv_web/live/user_live/index.html.heex:56 #, elixir-autogen, elixir-format msgid "Show" msgstr "Anzeigen" @@ -351,7 +351,7 @@ msgstr "Nicht gesetzt" msgid "Note" msgstr "Hinweis" -#: lib/mv_web/live/user_live/index.html.heex:56 +#: lib/mv_web/live/user_live/index.html.heex:52 #: lib/mv_web/live/user_live/show.ex:25 #, elixir-autogen, elixir-format msgid "OIDC ID" @@ -533,12 +533,12 @@ msgstr "Passwort" msgid "Password requirements" msgstr "Passwort-Anforderungen" -#: lib/mv_web/live/user_live/index.html.heex:25 +#: lib/mv_web/live/user_live/index.html.heex:21 #, elixir-autogen, elixir-format msgid "Select all users" msgstr "Alle Benutzer auswählen" -#: lib/mv_web/live/user_live/index.html.heex:39 +#: lib/mv_web/live/user_live/index.html.heex:35 #, elixir-autogen, elixir-format msgid "Select user" msgstr "Benutzer auswählen" @@ -552,3 +552,8 @@ msgstr "Passwort setzen" #, elixir-autogen, elixir-format msgid "User will be created without a password. Check 'Set Password' to add one." msgstr "Benutzer wird ohne Passwort erstellt. Aktivieren Sie 'Passwort setzen', um eines hinzuzufügen." + +#: lib/mv_web/auth_overrides.ex:30 +#, elixir-autogen, elixir-format +msgid "or" +msgstr "oder" diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot index fd064d2..a9bfb08 100644 --- a/priv/gettext/default.pot +++ b/priv/gettext/default.pot @@ -17,7 +17,7 @@ msgid "Actions" msgstr "" #: lib/mv_web/live/member_live/index.html.heex:77 -#: lib/mv_web/live/user_live/index.html.heex:69 +#: lib/mv_web/live/user_live/index.html.heex:65 #, elixir-autogen, elixir-format msgid "Are you sure?" msgstr "" @@ -36,14 +36,14 @@ msgid "City" msgstr "" #: lib/mv_web/live/member_live/index.html.heex:79 -#: lib/mv_web/live/user_live/index.html.heex:71 +#: lib/mv_web/live/user_live/index.html.heex:67 #, elixir-autogen, elixir-format msgid "Delete" msgstr "" #: lib/mv_web/live/member_live/index.html.heex:71 #: lib/mv_web/live/user_live/form.ex:109 -#: lib/mv_web/live/user_live/index.html.heex:63 +#: lib/mv_web/live/user_live/index.html.heex:59 #, elixir-autogen, elixir-format msgid "Edit" msgstr "" @@ -58,7 +58,7 @@ msgstr "" #: lib/mv_web/live/member_live/index.html.heex:58 #: lib/mv_web/live/member_live/show.ex:27 #: lib/mv_web/live/user_live/form.ex:14 -#: lib/mv_web/live/user_live/index.html.heex:48 +#: lib/mv_web/live/user_live/index.html.heex:44 #: lib/mv_web/live/user_live/show.ex:24 #, elixir-autogen, elixir-format msgid "Email" @@ -89,7 +89,7 @@ msgid "New Member" msgstr "" #: lib/mv_web/live/member_live/index.html.heex:68 -#: lib/mv_web/live/user_live/index.html.heex:60 +#: lib/mv_web/live/user_live/index.html.heex:56 #, elixir-autogen, elixir-format msgid "Show" msgstr "" @@ -352,7 +352,7 @@ msgstr "" msgid "Note" msgstr "" -#: lib/mv_web/live/user_live/index.html.heex:56 +#: lib/mv_web/live/user_live/index.html.heex:52 #: lib/mv_web/live/user_live/show.ex:25 #, elixir-autogen, elixir-format msgid "OIDC ID" @@ -534,12 +534,12 @@ msgstr "" msgid "Password requirements" msgstr "" -#: lib/mv_web/live/user_live/index.html.heex:25 +#: lib/mv_web/live/user_live/index.html.heex:21 #, elixir-autogen, elixir-format msgid "Select all users" msgstr "" -#: lib/mv_web/live/user_live/index.html.heex:39 +#: lib/mv_web/live/user_live/index.html.heex:35 #, elixir-autogen, elixir-format msgid "Select user" msgstr "" @@ -553,3 +553,8 @@ msgstr "" #, elixir-autogen, elixir-format msgid "User will be created without a password. Check 'Set Password' to add one." msgstr "" + +#: lib/mv_web/auth_overrides.ex:30 +#, elixir-autogen, elixir-format +msgid "or" +msgstr "" diff --git a/priv/gettext/en/LC_MESSAGES/auth.po b/priv/gettext/en/LC_MESSAGES/auth.po new file mode 100644 index 0000000..1e4e801 --- /dev/null +++ b/priv/gettext/en/LC_MESSAGES/auth.po @@ -0,0 +1,63 @@ +## "msgid"s in this file come from POT (.pot) files. +### +### Do not add, change, or remove "msgid"s manually here as +### they're tied to the ones in the corresponding POT file +### (with the same domain). +### +### Use "mix gettext.extract --merge" or "mix gettext.merge" +### to merge POT files into PO files. +msgid "" +msgstr "" +"Language: en\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Already have an account?" +msgstr "" + +msgid "Email or password was incorrect" +msgstr "" + +msgid "Email" +msgstr "" + +msgid "Forgot your password?" +msgstr "" + +msgid "If this user exists in our database you will contacted with a sign-in link shortly." +msgstr "" + +msgid "If this user exists in our system, you will be contacted with reset instructions shortly." +msgstr "" + +msgid "Need an account?" +msgstr "" + +msgid "Password" +msgstr "" + +msgid "Password Confirmation" +msgstr "" + +msgid "Request magic link" +msgstr "" + +msgid "Request password reset token" +msgstr "" + +msgid "Requesting ..." +msgstr "" + +msgid "Reset password with token" +msgstr "" + +msgid "Sign in" +msgstr "" + +msgid "Signing in ..." +msgstr "" + +msgid "Your password has successfully been reset" +msgstr "" + +msgid "Sign in with Rauthy" +msgstr "Sign in with Vereinscloud" diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po index 41a35b4..2f09378 100644 --- a/priv/gettext/en/LC_MESSAGES/default.po +++ b/priv/gettext/en/LC_MESSAGES/default.po @@ -17,7 +17,7 @@ msgid "Actions" msgstr "" #: lib/mv_web/live/member_live/index.html.heex:77 -#: lib/mv_web/live/user_live/index.html.heex:69 +#: lib/mv_web/live/user_live/index.html.heex:65 #, elixir-autogen, elixir-format msgid "Are you sure?" msgstr "" @@ -36,14 +36,14 @@ msgid "City" msgstr "" #: lib/mv_web/live/member_live/index.html.heex:79 -#: lib/mv_web/live/user_live/index.html.heex:71 +#: lib/mv_web/live/user_live/index.html.heex:67 #, elixir-autogen, elixir-format msgid "Delete" msgstr "" #: lib/mv_web/live/member_live/index.html.heex:71 #: lib/mv_web/live/user_live/form.ex:109 -#: lib/mv_web/live/user_live/index.html.heex:63 +#: lib/mv_web/live/user_live/index.html.heex:59 #, elixir-autogen, elixir-format msgid "Edit" msgstr "" @@ -58,7 +58,7 @@ msgstr "" #: lib/mv_web/live/member_live/index.html.heex:58 #: lib/mv_web/live/member_live/show.ex:27 #: lib/mv_web/live/user_live/form.ex:14 -#: lib/mv_web/live/user_live/index.html.heex:48 +#: lib/mv_web/live/user_live/index.html.heex:44 #: lib/mv_web/live/user_live/show.ex:24 #, elixir-autogen, elixir-format msgid "Email" @@ -89,7 +89,7 @@ msgid "New Member" msgstr "" #: lib/mv_web/live/member_live/index.html.heex:68 -#: lib/mv_web/live/user_live/index.html.heex:60 +#: lib/mv_web/live/user_live/index.html.heex:56 #, elixir-autogen, elixir-format msgid "Show" msgstr "" @@ -352,7 +352,7 @@ msgstr "" msgid "Note" msgstr "" -#: lib/mv_web/live/user_live/index.html.heex:56 +#: lib/mv_web/live/user_live/index.html.heex:52 #: lib/mv_web/live/user_live/show.ex:25 #, elixir-autogen, elixir-format msgid "OIDC ID" @@ -534,12 +534,12 @@ msgstr "Password" msgid "Password requirements" msgstr "Password requirements" -#: lib/mv_web/live/user_live/index.html.heex:25 +#: lib/mv_web/live/user_live/index.html.heex:21 #, elixir-autogen, elixir-format, fuzzy msgid "Select all users" msgstr "" -#: lib/mv_web/live/user_live/index.html.heex:39 +#: lib/mv_web/live/user_live/index.html.heex:35 #, elixir-autogen, elixir-format, fuzzy msgid "Select user" msgstr "" @@ -553,3 +553,8 @@ msgstr "Set Password" #, elixir-autogen, elixir-format 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." + +#: lib/mv_web/auth_overrides.ex:30 +#, elixir-autogen, elixir-format +msgid "or" +msgstr "" From 992addf0eaa7d3170483db96cf71fcae8c659fe7 Mon Sep 17 00:00:00 2001 From: carla Date: Wed, 6 Aug 2025 12:46:34 +0200 Subject: [PATCH 05/85] feat: memberslist as landing page --- lib/mv_web/router.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mv_web/router.ex b/lib/mv_web/router.ex index 82e9f00..4a6e3d9 100644 --- a/lib/mv_web/router.ex +++ b/lib/mv_web/router.ex @@ -47,7 +47,7 @@ defmodule MvWeb.Router do """ ash_authentication_live_session :authentication_required, on_mount: {MvWeb.LiveUserAuth, :live_user_required} do - get "/", PageController, :home + live "/", MemberLive.Index, :index live "/members", MemberLive.Index, :index live "/members/new", MemberLive.Form, :new From f0b0de000865a5bf8e3de846312d8757668edd24 Mon Sep 17 00:00:00 2001 From: carla Date: Thu, 14 Aug 2025 16:22:36 +0200 Subject: [PATCH 06/85] feat: set users locale --- assets/tailwind.config.js | 1 - lib/mv_web/auth_overrides.ex | 2 +- lib/mv_web/live_user_auth.ex | 8 ++++--- lib/mv_web/router.ex | 41 +++++++++++++++++++++++++++++++++++- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/assets/tailwind.config.js b/assets/tailwind.config.js index d87d46f..873d6d6 100644 --- a/assets/tailwind.config.js +++ b/assets/tailwind.config.js @@ -12,7 +12,6 @@ module.exports = { "../lib/mv_web.ex", "../lib/mv_web/**/*.*ex" ], - safelist: ['h-screen', 'px-6', 'h-screen', 'place-items-center', 'px-20', 'px-24'], // Classes that are used by AshAuthentication Sign-In Page are otherwise purged theme: { extend: { colors: { diff --git a/lib/mv_web/auth_overrides.ex b/lib/mv_web/auth_overrides.ex index 367982e..63cdcf9 100644 --- a/lib/mv_web/auth_overrides.ex +++ b/lib/mv_web/auth_overrides.ex @@ -17,7 +17,7 @@ defmodule MvWeb.AuthOverrides do # Avoid full-width for the Sign In Form override AshAuthentication.Phoenix.Components.SignIn do - set :root_class, "min-w-md" + set :root_class, "md:min-w-md" end # Replace banner logo with text diff --git a/lib/mv_web/live_user_auth.ex b/lib/mv_web/live_user_auth.ex index d211245..9b2a1a7 100644 --- a/lib/mv_web/live_user_auth.ex +++ b/lib/mv_web/live_user_auth.ex @@ -36,9 +36,11 @@ defmodule MvWeb.LiveUserAuth do end end - def on_mount(:live_no_user, _params, _session, socket) do - Gettext.put_locale(MvWeb.Gettext, "de") - {:cont, assign(socket, :locale, "de")} + def on_mount(:live_no_user, _params, session, socket) do + # Set the locale for not logged in user to set the language + locale = session["locale"] || "en" + Gettext.put_locale(MvWeb.Gettext, locale) + {:cont, assign(socket, :locale, locale)} if socket.assigns[:current_user] do {:halt, Phoenix.LiveView.redirect(socket, to: ~p"/")} diff --git a/lib/mv_web/router.ex b/lib/mv_web/router.ex index 4a6e3d9..bf2c071 100644 --- a/lib/mv_web/router.ex +++ b/lib/mv_web/router.ex @@ -139,8 +139,47 @@ defmodule MvWeb.Router do end defp set_locale(conn, _opts) do - locale = get_session(conn, :locale) || "de" + locale = + get_session(conn, :locale) || + extract_locale_from_headers(conn.req_headers) + Gettext.put_locale(MvWeb.Gettext, locale) + conn + |> put_session(:locale, locale) + |> assign(:locale, locale) end + + # Get locale from user + defp extract_locale_from_headers(headers) do + headers + |> Enum.find_value(fn + {"accept-language", value} -> value + _ -> nil + end) + |> parse_accept_language() + |> Enum.find(&supported_locale?/1) + |> fallback_locale() + end + + defp parse_accept_language(nil), do: [] + + defp parse_accept_language(header) do + header + |> String.split(",") + |> Enum.map(&String.trim/1) + |> Enum.map(fn lang -> + lang + # we only want the first part + |> String.split(";") + |> hd() + |> String.split("-") + |> hd() + end) + end + + # Our supported languages for now are german and english, english as fallback language + defp supported_locale?(locale), do: locale in ["en", "de"] + defp fallback_locale(nil), do: "en" + defp fallback_locale(locale), do: locale end From 7e2aa49674ad0bdefe121026f12965ce22e5b139 Mon Sep 17 00:00:00 2001 From: carla Date: Thu, 21 Aug 2025 13:40:16 +0200 Subject: [PATCH 07/85] docs: added comment --- lib/mv_web/live_user_auth.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/mv_web/live_user_auth.ex b/lib/mv_web/live_user_auth.ex index 9b2a1a7..c7b8684 100644 --- a/lib/mv_web/live_user_auth.ex +++ b/lib/mv_web/live_user_auth.ex @@ -37,7 +37,8 @@ defmodule MvWeb.LiveUserAuth do end def on_mount(:live_no_user, _params, session, socket) do - # Set the locale for not logged in user to set the language + # Set the locale for not logged in user to set the language in the Log-In Screen + # otherwise the locale is not taken for the Log-In Screen locale = session["locale"] || "en" Gettext.put_locale(MvWeb.Gettext, locale) {:cont, assign(socket, :locale, locale)} From 96085ea420052866de342687eca88b92c0b015de Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 14 Aug 2025 10:56:57 +0000 Subject: [PATCH 08/85] chore(deps): update mix dependencies --- mix.exs | 2 +- mix.lock | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/mix.exs b/mix.exs index 9b4fdd1..7d1797b 100644 --- a/mix.exs +++ b/mix.exs @@ -35,7 +35,7 @@ defmodule Mv.MixProject do # Type `mix help deps` for examples and options. defp deps do [ - {:tidewave, "~> 0.2", only: [:dev]}, + {:tidewave, "~> 0.3", only: [:dev]}, {:sourceror, "~> 1.8", only: [:dev, :test]}, {:live_debugger, "~> 0.3", only: [:dev]}, {:ash_admin, "~> 0.13"}, diff --git a/mix.lock b/mix.lock index 6599c01..46c9f3f 100644 --- a/mix.lock +++ b/mix.lock @@ -1,17 +1,17 @@ %{ - "ash": {:hex, :ash, "3.5.33", "2d4986050ce1c86f711b53f9bb40d6b227871f0cc771dab0b8b814a75a27c5ab", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, ">= 0.6.4 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 0.11", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.2.65 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, ">= 0.2.6 and < 1.0.0-0", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c7d1e043059967df749f9445bb903d62ed9c1defb5d45f6ddf32754b411ae93"}, - "ash_admin": {:hex, :ash_admin, "0.13.13", "d6f491587659c63c1e37b542bdef69c1e2dce9e13696e1fa537488983b98ac10", [:mix], [{:ash, ">= 3.4.63 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_phoenix, ">= 2.1.8 and < 3.0.0-0", [hex: :ash_phoenix, repo: "hexpm", optional: false]}, {:gettext, "~> 0.26", [hex: :gettext, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.1-rc", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}], "hexpm", "3378f54b5bfdbecc735ed848f137223692be4320975d01c23bd64e47db1f1a9a"}, + "ash": {:hex, :ash, "3.5.34", "e79e82dc3e3e66fb54a598eeba5feca2d1c3af6a0e752a3378cbad8d7a47dc6f", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, ">= 0.6.4 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 0.11", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.2.65 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, ">= 0.2.6 and < 1.0.0-0", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5cbf0a4d0ec1b6525b0782e4f5509c55dad446d657c635ceffe55f78a59132cd"}, + "ash_admin": {:hex, :ash_admin, "0.13.16", "6b30487e88b0a47b2da1c508b157be6d86b954ba464a01d412e6d5e047a53ad5", [:mix], [{:ash, ">= 3.4.63 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_phoenix, ">= 2.1.8 and < 3.0.0-0", [hex: :ash_phoenix, repo: "hexpm", optional: false]}, {:gettext, "~> 0.26", [hex: :gettext, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.1-rc", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}], "hexpm", "07a03d761b2029d8b1fefad815eb3cc525532ae9d440e7ca3f5c9f4c1ecb5d17"}, "ash_authentication": {:hex, :ash_authentication, "4.9.9", "23ec61bedc3157c258ece622c6f0f6a7645df275ff5e794d513cc6e8798471eb", [:mix], [{:argon2_elixir, "~> 4.0", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:ash, ">= 3.4.29 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_postgres, ">= 2.6.8 and < 3.0.0-0", [hex: :ash_postgres, repo: "hexpm", optional: true]}, {:assent, "~> 0.2.13", [hex: :assent, repo: "hexpm", optional: false]}, {:bcrypt_elixir, "~> 3.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: false]}, {:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:finch, "~> 0.19", [hex: :finch, repo: "hexpm", optional: false]}, {:igniter, "~> 0.4", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:joken, "~> 2.5", [hex: :joken, repo: "hexpm", optional: false]}, {:plug, "~> 1.13", [hex: :plug, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}], "hexpm", "ab8bd1277ff570425346dcf22dd14a059d9bbce0c28d24964b60e51fabaddda8"}, "ash_authentication_phoenix": {:hex, :ash_authentication_phoenix, "2.10.5", "9f3b1bee4a57f2269efea61e5efe55472683429b8a5bf1ebdd02d9748640f106", [:mix], [{:ash, "~> 3.0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_authentication, ">= 4.9.1 and < 5.0.0-0", [hex: :ash_authentication, repo: "hexpm", optional: false]}, {:ash_phoenix, ">= 2.3.11 and < 3.0.0-0", [hex: :ash_phoenix, repo: "hexpm", optional: false]}, {:bcrypt_elixir, "~> 3.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: false]}, {:gettext, "~> 0.26", [hex: :gettext, repo: "hexpm", optional: true]}, {:igniter, ">= 0.5.25 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_html_helpers, "~> 1.0", [hex: :phoenix_html_helpers, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:slugify, "~> 1.3", [hex: :slugify, repo: "hexpm", optional: false]}], "hexpm", "3f25778d126c7e759444df0855077802c93299457afdf26566f8de6320ba56da"}, - "ash_phoenix": {:hex, :ash_phoenix, "2.3.12", "34116f054ca4ef97b4badc73f028d78ee517692b713fd39f4c93f90bc2afd038", [:mix], [{:ash, ">= 3.5.13 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:igniter, "~> 0.6", [hex: :igniter, repo: "hexpm", optional: true]}, {:inertia, "~> 2.3", [hex: :inertia, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.6 or ~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.20.3 or ~> 1.0-rc.1", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:spark, ">= 2.2.29 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}], "hexpm", "27394e40b44ca06977e90bd0b38bce7bf41c6dab9fe2aa0b474fdb7c0c1f911b"}, - "ash_postgres": {:hex, :ash_postgres, "2.6.14", "8085b25864c63029a546ec7191d111f348405cb9d3a90677e52d805576319b55", [:mix], [{:ash, ">= 3.5.13 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_sql, ">= 0.2.72 and < 1.0.0-0", [hex: :ash_sql, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.13", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:igniter, ">= 0.6.14 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "0230343b959fb9cd24f76d1ecdbba90045d9625f03b33170ecb7c9ef011c9ac2"}, + "ash_phoenix": {:hex, :ash_phoenix, "2.3.13", "4470b18163b86123a8579ffe741c4f49b3c47bf99ce39a220c1bebae8e92053c", [:mix], [{:ash, ">= 3.5.13 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:igniter, "~> 0.6", [hex: :igniter, repo: "hexpm", optional: true]}, {:inertia, "~> 2.3", [hex: :inertia, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.6 or ~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.20.3 or ~> 1.0-rc.1", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:spark, ">= 2.2.29 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}], "hexpm", "b9e0bcbd76e63af0951af03b535b22cd256c360bb83fdf7b28ed73835086818a"}, + "ash_postgres": {:hex, :ash_postgres, "2.6.15", "29898b86689925bf5733ecbdeee0da8bfffad7127acf1e5c0ca15071b1881bbe", [:mix], [{:ash, ">= 3.5.13 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_sql, ">= 0.2.72 and < 1.0.0-0", [hex: :ash_sql, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.13", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:igniter, ">= 0.6.14 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "4298266c7bf6c621abc51d5ee8174b9f63c73fb9b2eda3812b91da5d8168b8d1"}, "ash_sql": {:hex, :ash_sql, "0.2.89", "ad4ad497263b586a7f3949ceea5d44620a36cb99a1ef0ff5f58f13a77d9b99ef", [:mix], [{:ash, ">= 3.5.25 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.9", [hex: :ecto_sql, repo: "hexpm", optional: false]}], "hexpm", "bd957aee95bbdf6326fc7a9212f9a2ab87329b99ee3646c373a87bb3c9968566"}, "assent": {:hex, :assent, "0.2.13", "11226365d2d8661d23e9a2cf94d3255e81054ff9d88ac877f28bfdf38fa4ef31", [:mix], [{:certifi, ">= 0.0.0", [hex: :certifi, repo: "hexpm", optional: true]}, {:finch, "~> 0.15", [hex: :finch, repo: "hexpm", optional: true]}, {:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: true]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:req, "~> 0.4", [hex: :req, repo: "hexpm", optional: true]}, {:ssl_verify_fun, ">= 0.0.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: true]}], "hexpm", "bf9f351b01dd6bceea1d1f157f05438f6765ce606e6eb8d29296003d29bf6eab"}, - "bandit": {:hex, :bandit, "1.7.0", "d1564f30553c97d3e25f9623144bb8df11f3787a26733f00b21699a128105c0c", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "3e2f7a98c7a11f48d9d8c037f7177cd39778e74d55c7af06fe6227c742a8168a"}, + "bandit": {:hex, :bandit, "1.8.0", "c2e93d7e3c5c794272fa4623124f827c6f24b643acc822be64c826f9447d92fb", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "8458ff4eed20ff2a2ea69d4854883a077c33ea42b51f6811b044ceee0fa15422"}, "bcrypt_elixir": {:hex, :bcrypt_elixir, "3.3.2", "d50091e3c9492d73e17fc1e1619a9b09d6a5ef99160eb4d736926fd475a16ca3", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "471be5151874ae7931911057d1467d908955f93554f7a6cd1b7d804cac8cef53"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, - "castore": {:hex, :castore, "1.0.14", "4582dd7d630b48cf5e1ca8d3d42494db51e406b7ba704e81fbd401866366896a", [:mix], [], "hexpm", "7bc1b65249d31701393edaaac18ec8398d8974d52c647b7904d01b964137b9f4"}, - "cc_precompiler": {:hex, :cc_precompiler, "0.1.10", "47c9c08d8869cf09b41da36538f62bc1abd3e19e41701c2cea2675b53c704258", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f6e046254e53cd6b41c6bacd70ae728011aa82b2742a80d6e2214855c6e06b22"}, + "castore": {:hex, :castore, "1.0.15", "8aa930c890fe18b6fe0a0cff27b27d0d4d231867897bd23ea772dee561f032a3", [:mix], [], "hexpm", "96ce4c69d7d5d7a0761420ef743e2f4096253931a3ba69e5ff8ef1844fe446d3"}, + "cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"}, "circular_buffer": {:hex, :circular_buffer, "1.0.0", "25c004da0cba7bd8bc1bdabded4f9a902d095e20600fd15faf1f2ffbaea18a07", [:mix], [], "hexpm", "c829ec31c13c7bafd1f546677263dff5bfb006e929f25635878ac3cfba8749e5"}, "comeonin": {:hex, :comeonin, "5.5.1", "5113e5f3800799787de08a6e0db307133850e635d34e9fab23c70b6501669510", [:mix], [], "hexpm", "65aac8f19938145377cee73973f192c5645873dcf550a8a6b18187d17c13ccdb"}, "credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [: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", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"}, @@ -28,19 +28,19 @@ "expo": {:hex, :expo, "1.1.0", "f7b9ed7fb5745ebe1eeedf3d6f29226c5dd52897ac67c0f8af62a07e661e5c75", [:mix], [], "hexpm", "fbadf93f4700fb44c331362177bdca9eeb8097e8b0ef525c9cc501cb9917c960"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, "finch": {:hex, :finch, "0.20.0", "5330aefb6b010f424dcbbc4615d914e9e3deae40095e73ab0c1bb0968933cadf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2658131a74d051aabfcba936093c903b8e89da9a1b63e430bee62045fa9b2ee2"}, - "fine": {:hex, :fine, "0.1.2", "85cf7dd190c7c6c54c2840754ae977c9acc0417316255b674fad9f2678e4ecc7", [:mix], [], "hexpm", "9113531982c2b60dbea6c7233917ddf16806947cd7104b5d03011bf436ca3072"}, + "fine": {:hex, :fine, "0.1.4", "b19a89c1476c7c57afb5f9314aed5960b5bc95d5277de4cb5ee8e1d1616ce379", [:mix], [], "hexpm", "be3324cc454a42d80951cf6023b9954e9ff27c6daa255483b3e8d608670303f5"}, "gettext": {:hex, :gettext, "0.26.2", "5978aa7b21fada6deabf1f6341ddba50bc69c999e812211903b169799208f2a8", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "aa978504bcf76511efdc22d580ba08e2279caab1066b76bb9aa81c4a1e0a32a5"}, "glob_ex": {:hex, :glob_ex, "0.1.11", "cb50d3f1ef53f6ca04d6252c7fde09fd7a1cf63387714fe96f340a1349e62c93", [:mix], [], "hexpm", "342729363056e3145e61766b416769984c329e4378f1d558b63e341020525de4"}, "heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "0435d4ca364a608cc75e2f8683d374e55abbae26", [tag: "v2.2.0", sparse: "optimized", depth: 1]}, "hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"}, - "igniter": {:hex, :igniter, "0.6.25", "e2774a4605c2bc9fc38f689232604aea0fc925c7966ae8e928fd9ea2fa9d300c", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:phx_new, "~> 1.7", [hex: :phx_new, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:rewrite, ">= 1.1.1 and < 2.0.0-0", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "b1916e1e45796d5c371c7671305e81277231617eb58b1c120915aba237fbce6a"}, + "igniter": {:hex, :igniter, "0.6.27", "a7c01062db56f5c5ac0f36ff8ef3cce1d61cd6bf59e50c52f4a38dc926aa9728", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:phx_new, "~> 1.7", [hex: :phx_new, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:rewrite, ">= 1.1.1 and < 2.0.0-0", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "d1eda5271932dcb9f00f94936c3dc12a2b96466f895f4b3fb82a0caada6d6447"}, "iterex": {:hex, :iterex, "0.1.2", "58f9b9b9a22a55cbfc7b5234a9c9c63eaac26d276b3db80936c0e1c60355a5a6", [:mix], [], "hexpm", "2e103b8bcc81757a9af121f6dc0df312c9a17220f302b1193ef720460d03029d"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "joken": {:hex, :joken, "2.6.2", "5daaf82259ca603af4f0b065475099ada1b2b849ff140ccd37f4b6828ca6892a", [:mix], [{:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5134b5b0a6e37494e46dbf9e4dad53808e5e787904b7c73972651b51cce3d72b"}, "jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"}, - "lazy_html": {:hex, :lazy_html, "0.1.3", "8b9c8c135e95f7bc483de6195c4e1c0b2c913a5e2c57353ef4e82703b7ac8bd1", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.9.0", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:fine, "~> 0.1.0", [hex: :fine, repo: "hexpm", optional: false]}], "hexpm", "5f96f29587dcfed8a22281e8c44c6607e958ba821d90b9dfc003d1ef610f7d07"}, + "lazy_html": {:hex, :lazy_html, "0.1.6", "bff2c5901b008fd75d41f777eb54a19fcf47544cc8c5e5509d84c2b3ea471c69", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.9.0", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:fine, "~> 0.1.0", [hex: :fine, repo: "hexpm", optional: false]}], "hexpm", "e04bddfaa09d38e5c3e39278a470550faa7d45d0a30ebc87eb2bd740c364aaaa"}, "libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"}, - "live_debugger": {:hex, :live_debugger, "0.3.1", "4b4d36481c3b0a49ec082c8268d37974ece34d2091ac323ccc0c906eb0c0d032", [:mix], [{:igniter, ">= 0.5.40 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:phoenix_live_view, "~> 0.20.4 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "e50836495134b0dde98dc96919340749130ecb83618ea99d63a3a58ed1dcc27d"}, + "live_debugger": {:hex, :live_debugger, "0.3.2", "b67baa8ed6a4329fe0c6aaf21a403cce4d0bac9b33d90707fe2609108614ac69", [:mix], [{:igniter, ">= 0.5.40 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:phoenix_live_view, "~> 0.20.4 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "5050b37af05a2b84d429e7256a41d3612283c4c802edd23e6eeb4e0b6fc2a712"}, "luhn": {:hex, :luhn, "0.3.3", "5aa0c6a32c2db4b9db9f9b883ba8301c1ae169d57199b9e6cb1ba2707bc51d96", [:mix], [], "hexpm", "3e823a913a25aab51352c727f135278d22954874d5f0835be81ed4fec3daf78d"}, "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, "mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"}, @@ -48,13 +48,13 @@ "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "owl": {:hex, :owl, "0.12.2", "65906b525e5c3ef51bab6cba7687152be017aebe1da077bb719a5ee9f7e60762", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "6398efa9e1fea70a04d24231e10dcd66c1ac1aa2da418d20ef5357ec61de2880"}, - "phoenix": {:hex, :phoenix, "1.8.0-rc.4", "6c18c1e07938d3d8dbb957ed0d193fa591718a2997058f6883cfa7447f07612a", [:mix], [{:bandit, "~> 1.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "754c8caf0d1332bc691f826d678b192b3f78cfeb01df2f623683e308b363dc41"}, + "phoenix": {:hex, :phoenix, "1.8.0", "dc5d256bb253110266ded8c4a6a167e24fabde2e14b8e474d262840ae8d8ea18", [:mix], [{:bandit, "~> 1.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "15f6e9cb76646ad8d9f2947240519666fc2c4f29f8a93ad9c7664916ab4c167b"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.6.5", "c4ef322acd15a574a8b1a08eff0ee0a85e73096b53ce1403b6563709f15e1cea", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "26ec3208eef407f31b748cadd044045c6fd485fbff168e35963d2f9dfff28d4b"}, "phoenix_html": {:hex, :phoenix_html, "4.2.1", "35279e2a39140068fc03f8874408d58eef734e488fc142153f055c5454fd1c08", [:mix], [], "hexpm", "cff108100ae2715dd959ae8f2a8cef8e20b593f8dfd031c9cba92702cf23e053"}, "phoenix_html_helpers": {:hex, :phoenix_html_helpers, "1.0.1", "7eed85c52eff80a179391036931791ee5d2f713d76a81d0d2c6ebafe1e11e5ec", [:mix], [{:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "cffd2385d1fa4f78b04432df69ab8da63dc5cf63e07b713a4dcf36a3740e3090"}, "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.7", "405880012cb4b706f26dd1c6349125bfc903fb9e44d1ea668adaf4e04d4884b7", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "3a8625cab39ec261d48a13b7468dc619c0ede099601b084e343968309bd4d7d7"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.6.0", "2791fac0e2776b640192308cc90c0dbcf67843ad51387ed4ecae2038263d708d", [:mix], [{:file_system, "~> 0.2.10 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "b3a1fa036d7eb2f956774eda7a7638cf5123f8f2175aca6d6420a7f95e598e1c"}, - "phoenix_live_view": {:hex, :phoenix_live_view, "1.1.0-rc.4", "1e933da296a80c0f57689b25db8711fc47feb452ac5de4b4824e8e64bccae9f9", [:mix], [{:igniter, ">= 0.6.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:lazy_html, "~> 0.1.0", [hex: :lazy_html, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0-rc", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4747a143c9b494b19f6ac58b919be46ff773066efe4882ee37ba0fd272f673c2"}, + "phoenix_live_view": {:hex, :phoenix_live_view, "1.1.8", "d283d5e047e6c013182a3833e99ff33942e3a8076f9f984c337ea04cc53e8206", [:mix], [{:igniter, ">= 0.6.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:lazy_html, "~> 0.1.0", [hex: :lazy_html, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0-rc", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6184cf1e82fe6627d40cfa62236133099438513710d30358f4c085c16ecb84b4"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, "phoenix_view": {:hex, :phoenix_view, "2.0.4", "b45c9d9cf15b3a1af5fb555c674b525391b6a1fe975f040fb4d913397b31abf4", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "4e992022ce14f31fe57335db27a28154afcc94e9983266835bb3040243eb620b"}, @@ -67,7 +67,7 @@ "slugify": {:hex, :slugify, "1.3.1", "0d3b8b7e5c1eeaa960e44dce94382bee34a39b3ea239293e457a9c5b47cc6fd3", [:mix], [], "hexpm", "cb090bbeb056b312da3125e681d98933a360a70d327820e4b7f91645c4d8be76"}, "sobelow": {:hex, :sobelow, "0.14.0", "dd82aae8f72503f924fe9dd97ffe4ca694d2f17ec463dcfd365987c9752af6ee", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "7ecf91e298acfd9b24f5d761f19e8f6e6ac585b9387fb6301023f1f2cd5eed5f"}, "sourceror": {:hex, :sourceror, "1.10.0", "38397dedbbc286966ec48c7af13e228b171332be1ad731974438c77791945ce9", [:mix], [], "hexpm", "29dbdfc92e04569c9d8e6efdc422fc1d815f4bd0055dc7c51b8800fb75c4b3f1"}, - "spark": {:hex, :spark, "2.2.67", "67626cb9f59ea4b1c5aa85d4afdd025e0740cbd49ed82665d0a40ff007d7fd4b", [:mix], [{:igniter, ">= 0.3.64 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: true]}, {:sourceror, "~> 1.2", [hex: :sourceror, repo: "hexpm", optional: true]}], "hexpm", "c8575402e3afc66871362e821bece890536d16319cdb758c5fb2d1250182e46f"}, + "spark": {:hex, :spark, "2.2.68", "4c4547c88d73311e3157bc402ab27f3a7bbd5e55a2ee92bcf7cd3a0a475d201e", [:mix], [{:igniter, ">= 0.3.64 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: true]}, {:sourceror, "~> 1.2", [hex: :sourceror, repo: "hexpm", optional: true]}], "hexpm", "2fa4dd89801415a098a421589633f0e3df7ed9ff4046e80a65d35a413bc0d194"}, "spitfire": {:hex, :spitfire, "0.2.1", "29e154873f05444669c7453d3d931820822cbca5170e88f0f8faa1de74a79b47", [:mix], [], "hexpm", "6eeed75054a38341b2e1814d41bb0a250564092358de2669fdb57ff88141d91b"}, "splode": {:hex, :splode, "0.2.9", "3a2776e187c82f42f5226b33b1220ccbff74f4bcc523dd4039c804caaa3ffdc7", [:mix], [], "hexpm", "8002b00c6e24f8bd1bcced3fbaa5c33346048047bb7e13d2f3ad428babbd95c3"}, "stream_data": {:hex, :stream_data, "1.2.0", "58dd3f9e88afe27dc38bef26fce0c84a9e7a96772b2925c7b32cd2435697a52b", [:mix], [], "hexpm", "eb5c546ee3466920314643edf68943a5b14b32d1da9fe01698dc92b73f89a9ed"}, @@ -79,7 +79,7 @@ "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"}, "thousand_island": {:hex, :thousand_island, "1.3.14", "ad45ebed2577b5437582bcc79c5eccd1e2a8c326abf6a3464ab6c06e2055a34a", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d0d24a929d31cdd1d7903a4fe7f2409afeedff092d277be604966cd6aa4307ef"}, - "tidewave": {:hex, :tidewave, "0.2.0", "e98378803e535d3035138e4b354dcfca26b7f862fd44cffef5aa697b814c0b0b", [:mix], [{:circular_buffer, "~> 0.4 or ~> 1.0", [hex: :circular_buffer, repo: "hexpm", optional: false]}, {:igniter, ">= 0.5.47 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:plug, "~> 1.17", [hex: :plug, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "6ad11829f4600cd69955ffc66935e6456b775fea095172147244ba6f65986735"}, + "tidewave": {:hex, :tidewave, "0.4.0", "8a406890b91b892d7bfdba16f7be72a97cd7e8719f71a85a610623ef80263325", [: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.0", [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", "3e68b3d39cfe5ce1aba705af7b24aac3926413b004d5ece38642c5f17dd34625"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, "websock_adapter": {:hex, :websock_adapter, "0.5.8", "3b97dc94e407e2d1fc666b2fb9acf6be81a1798a2602294aac000260a7c4a47d", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "315b9a1865552212b5f35140ad194e67ce31af45bcee443d4ecb96b5fd3f3782"}, "yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"}, From 7118782a2dabe20fef50afe52e2ca357c5f61251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Eppl=C3=A9e?= Date: Thu, 21 Aug 2025 14:09:03 +0200 Subject: [PATCH 09/85] Add seed data for members --- priv/repo/seeds.exs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 306b627..cb38969 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -47,3 +47,48 @@ end Accounts.create_user!(%{email: "admin@mv.local"}, upsert?: true, upsert_identity: :unique_email) |> Ash.Changeset.for_update(:admin_set_password, %{password: "testpassword"}) |> Ash.update!() + +# Create sample members for testing +for member_attrs <- [ + %{ + first_name: "Hans", + last_name: "Müller", + email: "hans.mueller@example.de", + birth_date: ~D[1985-06-15], + join_date: ~D[2023-01-15], + paid: true, + phone_number: "+49301234567", + city: "München", + street: "Hauptstraße", + house_number: "42", + postal_code: "80331" + }, + %{ + first_name: "Greta", + last_name: "Schmidt", + email: "greta.schmidt@example.de", + birth_date: ~D[1990-03-22], + join_date: ~D[2023-02-01], + paid: false, + phone_number: "+49309876543", + city: "Hamburg", + street: "Lindenstraße", + house_number: "17", + postal_code: "20095", + notes: "Interessiert an Fortgeschrittenen-Kursen" + }, + %{ + first_name: "Friedrich", + last_name: "Wagner", + email: "friedrich.wagner@example.de", + birth_date: ~D[1978-11-08], + join_date: ~D[2022-11-10], + paid: true, + phone_number: "+49301122334", + city: "Berlin", + street: "Kastanienallee", + house_number: "8" + } + ] do + Membership.create_member!(member_attrs) +end From a3746dfaaa5aa9e32a7b6dd82dad0d494d86e5ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Eppl=C3=A9e?= Date: Thu, 11 Sep 2025 11:49:23 +0200 Subject: [PATCH 10/85] Explicitly require ash authentication settings Previously, we'd rely on defaults for configuring user token authentication. With these changes, we explicitly require :session_identifier and :require_token_presence_for_authentication to be configured in the application environment to make sure the system is configured the way it should be. --- lib/accounts/user.ex | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/accounts/user.ex b/lib/accounts/user.ex index 9294526..b085407 100644 --- a/lib/accounts/user.ex +++ b/lib/accounts/user.ex @@ -19,16 +19,15 @@ defmodule Mv.Accounts.User do Currently password and SSO with Rauthy as OIDC provider """ authentication do - session_identifier Application.compile_env(:mv, :session_identifier, :jti) + session_identifier Application.compile_env!(:mv, :session_identifier) tokens do enabled? true token_resource Mv.Accounts.Token - require_token_presence_for_authentication? Application.compile_env( + require_token_presence_for_authentication? Application.compile_env!( :mv, - :require_token_presence_for_authentication, - false + :require_token_presence_for_authentication ) store_all_tokens? true From dd03000428aa82090ad23a1c20f31ea02a4b37f6 Mon Sep 17 00:00:00 2001 From: carla Date: Wed, 17 Sep 2025 13:34:14 +0200 Subject: [PATCH 11/85] chore: adds tsvector to members --- lib/membership/member.ex | 2 + ...0250912085235_AddSearchVectorToMembers.exs | 60 ++++++ .../repo/members/20250912085235.json | 199 ++++++++++++++++++ 3 files changed, 261 insertions(+) create mode 100644 priv/repo/migrations/20250912085235_AddSearchVectorToMembers.exs create mode 100644 priv/resource_snapshots/repo/members/20250912085235.json diff --git a/lib/membership/member.ex b/lib/membership/member.ex index 583f173..14e8708 100644 --- a/lib/membership/member.ex +++ b/lib/membership/member.ex @@ -166,6 +166,8 @@ defmodule Mv.Membership.Member do attribute :postal_code, :string do allow_nil? true end + + attribute :search_vector, AshPostgres.Tsvector, writable?: false, public?: false, select_by_default?: false end relationships do diff --git a/priv/repo/migrations/20250912085235_AddSearchVectorToMembers.exs b/priv/repo/migrations/20250912085235_AddSearchVectorToMembers.exs new file mode 100644 index 0000000..126f369 --- /dev/null +++ b/priv/repo/migrations/20250912085235_AddSearchVectorToMembers.exs @@ -0,0 +1,60 @@ +defmodule Mv.Repo.Migrations.AddSearchVectorToMembers do + @moduledoc """ + Updates resources based on their most recent snapshots. + + This file was autogenerated with `mix ash_postgres.generate_migrations` + """ + + use Ecto.Migration + + def up do + alter table(:members) do + add :search_vector, :tsvector + end + + execute(""" + CREATE INDEX members_search_vector_idx + ON members + USING GIN (search_vector) + """) + + # Eigene Trigger-Funktion mit Gewichtung + execute(""" + CREATE FUNCTION members_search_vector_trigger() RETURNS trigger AS $$ + BEGIN + NEW.search_vector := + setweight(to_tsvector('simple', coalesce(NEW.first_name, '')), 'A') || + setweight(to_tsvector('simple', coalesce(NEW.last_name, '')), 'A') || + setweight(to_tsvector('simple', coalesce(NEW.email, '')), 'B') || + setweight(to_tsvector('simple', coalesce(NEW.birth_date::text, '')), 'C') || + setweight(to_tsvector('simple', coalesce(NEW.phone_number, '')), 'C') || + setweight(to_tsvector('simple', coalesce(NEW.join_date::text, '')), 'D') || + setweight(to_tsvector('simple', coalesce(NEW.exit_date::text, '')), 'D') || + setweight(to_tsvector('simple', coalesce(NEW.notes, '')), 'B') || + setweight(to_tsvector('simple', coalesce(NEW.city, '')), 'C') || + setweight(to_tsvector('simple', coalesce(NEW.street, '')), 'C') || + setweight(to_tsvector('simple', coalesce(NEW.house_number::text, '')), 'C') || + setweight(to_tsvector('simple', coalesce(NEW.postal_code::text, '')), 'C'); + RETURN NEW; + END + $$ LANGUAGE plpgsql; + """) + + execute(""" + CREATE TRIGGER update_search_vector + BEFORE INSERT OR UPDATE ON members + FOR EACH ROW + EXECUTE FUNCTION members_search_vector_trigger() + """) + end + + def down do + execute("DROP TRIGGER IF EXISTS update_search_vector ON members") + execute("DROP FUNCTION IF EXISTS members_search_vector_trigger()") + execute("DROP INDEX IF EXISTS members_search_vector_idx") + + alter table(:members) do + remove :search_vector + end + end +end diff --git a/priv/resource_snapshots/repo/members/20250912085235.json b/priv/resource_snapshots/repo/members/20250912085235.json new file mode 100644 index 0000000..a8b86da --- /dev/null +++ b/priv/resource_snapshots/repo/members/20250912085235.json @@ -0,0 +1,199 @@ +{ + "attributes": [ + { + "allow_nil?": false, + "default": "fragment(\"uuid_generate_v7()\")", + "generated?": false, + "precision": null, + "primary_key?": true, + "references": null, + "scale": null, + "size": null, + "source": "id", + "type": "uuid" + }, + { + "allow_nil?": false, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": null, + "scale": null, + "size": null, + "source": "first_name", + "type": "text" + }, + { + "allow_nil?": false, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": null, + "scale": null, + "size": null, + "source": "last_name", + "type": "text" + }, + { + "allow_nil?": false, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": null, + "scale": null, + "size": null, + "source": "email", + "type": "text" + }, + { + "allow_nil?": true, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": null, + "scale": null, + "size": null, + "source": "birth_date", + "type": "date" + }, + { + "allow_nil?": true, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": null, + "scale": null, + "size": null, + "source": "paid", + "type": "boolean" + }, + { + "allow_nil?": true, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": null, + "scale": null, + "size": null, + "source": "phone_number", + "type": "text" + }, + { + "allow_nil?": true, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": null, + "scale": null, + "size": null, + "source": "join_date", + "type": "date" + }, + { + "allow_nil?": true, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": null, + "scale": null, + "size": null, + "source": "exit_date", + "type": "date" + }, + { + "allow_nil?": true, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": null, + "scale": null, + "size": null, + "source": "notes", + "type": "text" + }, + { + "allow_nil?": true, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": null, + "scale": null, + "size": null, + "source": "city", + "type": "text" + }, + { + "allow_nil?": true, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": null, + "scale": null, + "size": null, + "source": "street", + "type": "text" + }, + { + "allow_nil?": true, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": null, + "scale": null, + "size": null, + "source": "house_number", + "type": "text" + }, + { + "allow_nil?": true, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": null, + "scale": null, + "size": null, + "source": "postal_code", + "type": "text" + }, + { + "allow_nil?": true, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": null, + "scale": null, + "size": null, + "source": "search_vectors", + "type": "tsvector" + } + ], + "base_filter": null, + "check_constraints": [], + "custom_indexes": [], + "custom_statements": [], + "has_create_action": true, + "hash": "3B162FD69B92BF8258DB56BA0CBB6108FBE996B1F7231C5F2D9EC53D956EFC75", + "identities": [], + "multitenancy": { + "attribute": null, + "global": null, + "strategy": null + }, + "repo": "Elixir.Mv.Repo", + "schema": null, + "table": "members" +} \ No newline at end of file From 78588cbad9c667bd9990f34b63dffaa46844a272 Mon Sep 17 00:00:00 2001 From: carla Date: Wed, 17 Sep 2025 14:36:13 +0200 Subject: [PATCH 12/85] feat: adds SearchBar Live Component --- .../live/components/search_bar_component.ex | 62 +++++++++++++++++++ lib/mv_web/live/member_live/index.ex | 28 +++++++++ lib/mv_web/live/member_live/index.html.heex | 7 +++ 3 files changed, 97 insertions(+) create mode 100644 lib/mv_web/live/components/search_bar_component.ex diff --git a/lib/mv_web/live/components/search_bar_component.ex b/lib/mv_web/live/components/search_bar_component.ex new file mode 100644 index 0000000..b1c4a10 --- /dev/null +++ b/lib/mv_web/live/components/search_bar_component.ex @@ -0,0 +1,62 @@ +defmodule MvWeb.Components.SearchBarComponent do + @moduledoc """ + Provides the SearchBar Live-Component. + + - uses the DaisyUI search input field + - sends search_changed event to parent live view with a query + """ + use MvWeb, :live_component + + @impl true + def update(assigns, socket) do + socket = + socket + |> assign_new(:query, fn -> "" end) + |> assign_new(:placeholder, fn -> gettext("Search...") end) + + {:ok, socket} + end + + @impl true + def render(assigns) do + ~H""" +
+ +
+ """ + end + + @impl true + # Function to handle the search + def handle_event("search", %{"query" => q}, socket) do + # Forward a high level message to the parent + send(self(), {:search_changed, q}) + {:noreply, assign(socket, :query, q)} + end +end diff --git a/lib/mv_web/live/member_live/index.ex b/lib/mv_web/live/member_live/index.ex index 476abd1..d238436 100644 --- a/lib/mv_web/live/member_live/index.ex +++ b/lib/mv_web/live/member_live/index.ex @@ -1,5 +1,7 @@ defmodule MvWeb.MemberLive.Index do use MvWeb, :live_view + import Ash.Expr + import Ash.Query import MvWeb.TableComponents @impl true @@ -10,12 +12,38 @@ defmodule MvWeb.MemberLive.Index do {:ok, socket |> assign(:page_title, gettext("Members")) + |> assign(:query, "") |> assign(:sort_field, :first_name) |> assign(:sort_order, :asc) |> assign(:members, sorted) |> assign(:selected_members, [])} end + # ----------------------------------------------------------------- + # Receive messages from any toolbar component + # ----------------------------------------------------------------- + + # Function to handle search + @impl true + def handle_info({:search_changed, q}, socket) do + members = + Mv.Membership.Member + |> Ash.Query.filter( + expr(fragment("search_vector @@ plainto_tsquery('simple', ?)", ^q)) + ) + |> Ash.read!() + + IO.inspect(members) + {:noreply, + socket + |> assign(:query, q) + |> assign(:members, members)} + end + + # ----------------------------------------------------------------- + # Handle Events + # ----------------------------------------------------------------- + @impl true def handle_event("delete", %{"id" => id}, socket) do member = Ash.get!(Mv.Membership.Member, id) diff --git a/lib/mv_web/live/member_live/index.html.heex b/lib/mv_web/live/member_live/index.html.heex index fc38889..aa7a820 100644 --- a/lib/mv_web/live/member_live/index.html.heex +++ b/lib/mv_web/live/member_live/index.html.heex @@ -8,6 +8,13 @@ + <.live_component + module={MvWeb.Components.SearchBarComponent} + id="search-bar" + query={@query} + placeholder={gettext("Search...")} + /> + <.table id="members" rows={@members} From 53f6b62289b42444d04d3d1ddb6f1fdae7f7797d Mon Sep 17 00:00:00 2001 From: carla Date: Wed, 17 Sep 2025 14:36:50 +0200 Subject: [PATCH 13/85] test: updated tests for member and search bar --- .../components/search_bar_component_test.exs | 34 +++++++++++++++++++ test/mv_web/member_live/index_test.exs | 13 +++++++ 2 files changed, 47 insertions(+) create mode 100644 test/mv_web/components/search_bar_component_test.exs diff --git a/test/mv_web/components/search_bar_component_test.exs b/test/mv_web/components/search_bar_component_test.exs new file mode 100644 index 0000000..d84f78e --- /dev/null +++ b/test/mv_web/components/search_bar_component_test.exs @@ -0,0 +1,34 @@ +defmodule MvWeb.Components.SearchBarComponentTest do + use MvWeb.ConnCase, async: true + import Phoenix.LiveViewTest + + describe "SearchBarComponent" do + test "renders with placeholder", %{conn: conn} do + conn = conn_with_oidc_user(conn) + {:ok, view, _html} = live(conn, "/members") + + assert has_element?(view, "input[placeholder='Search...']") + end + + test "updates query when user types", %{conn: conn} do + conn = conn_with_oidc_user(conn) + {:ok, view, _html} = live(conn, "/members") + + # simulate search input and check that correct user is listed + html = + view + |> element("form[role=search]") + |> render_change(%{"query" => "Friedrich"}) + + assert html =~ "Friedrich" + + # simulate search input and check that not matching user is not shown + html = + view + |> element("form[role=search]") + |> render_change(%{"query" => "Greta"}) + + refute html =~ "Friedrich" + end + end +end diff --git a/test/mv_web/member_live/index_test.exs b/test/mv_web/member_live/index_test.exs index e3e77dc..e10c685 100644 --- a/test/mv_web/member_live/index_test.exs +++ b/test/mv_web/member_live/index_test.exs @@ -73,4 +73,17 @@ defmodule MvWeb.MemberLive.IndexTest do assert has_element?(index_view, "#flash-group", "Member create successfully") end + + test "handle_info(:search_changed) updates assigns with search results", %{conn: conn} do + conn = conn_with_oidc_user(conn) + {:ok, view, _html} = live(conn, "/members") + + send(view.pid, {:search_changed, "Friedrich"}) + + # State aus dem LiveView-Prozess holen + state = :sys.get_state(view.pid) + + assert state.socket.assigns.query == "Friedrich" + assert is_list(state.socket.assigns.members) + end end From 02b30847893767202f46c05ccbd0a7c2fb13e113 Mon Sep 17 00:00:00 2001 From: carla Date: Wed, 17 Sep 2025 14:37:04 +0200 Subject: [PATCH 14/85] formatting --- lib/membership/member.ex | 5 ++++- lib/mv_web/live/components/search_bar_component.ex | 3 +-- lib/mv_web/live/member_live/index.ex | 9 ++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/membership/member.ex b/lib/membership/member.ex index 14e8708..7fe69da 100644 --- a/lib/membership/member.ex +++ b/lib/membership/member.ex @@ -167,7 +167,10 @@ defmodule Mv.Membership.Member do allow_nil? true end - attribute :search_vector, AshPostgres.Tsvector, writable?: false, public?: false, select_by_default?: false + attribute :search_vector, AshPostgres.Tsvector, + writable?: false, + public?: false, + select_by_default?: false end relationships do diff --git a/lib/mv_web/live/components/search_bar_component.ex b/lib/mv_web/live/components/search_bar_component.ex index b1c4a10..dd7341a 100644 --- a/lib/mv_web/live/components/search_bar_component.ex +++ b/lib/mv_web/live/components/search_bar_component.ex @@ -20,8 +20,7 @@ defmodule MvWeb.Components.SearchBarComponent do @impl true def render(assigns) do ~H""" -
+