diff --git a/Justfile b/Justfile index f44be78..19a93bf 100644 --- a/Justfile +++ b/Justfile @@ -9,6 +9,7 @@ migrate-database: reset-database: mix ash.reset + MIX_ENV=test mix ash.reset seed-database: mix run priv/repo/seeds.exs @@ -18,6 +19,10 @@ start-database: ci-dev: lint audit test +gettext: + mix gettext.extract + mix gettext.merge priv/gettext + lint: mix format --check-formatted mix compile --warnings-as-errors diff --git a/lib/membership/member.ex b/lib/membership/member.ex index 0538f45..ec2b16f 100644 --- a/lib/membership/member.ex +++ b/lib/membership/member.ex @@ -14,6 +14,23 @@ defmodule Mv.Membership.Member do create :create_member do primary? true argument :properties, {:array, :map} + + accept [ + :first_name, + :last_name, + :email, + :birth_date, + :paid, + :phone_number, + :join_date, + :exit_date, + :notes, + :city, + :street, + :house_number, + :postal_code + ] + change manage_relationship(:properties, type: :create) end @@ -21,12 +38,138 @@ defmodule Mv.Membership.Member do primary? true require_atomic? false argument :properties, {:array, :map} + + accept [ + :first_name, + :last_name, + :email, + :birth_date, + :paid, + :phone_number, + :join_date, + :exit_date, + :notes, + :city, + :street, + :house_number, + :postal_code + ] + change manage_relationship(:properties, on_match: :update, on_no_match: :create) end end + validations do + # Required fields are covered by allow_nil? false + + # First name and last name must not be empty + validate present(:first_name) + validate present(:last_name) + validate present(:email) + + # Birth date not in the future + validate compare(:birth_date, less_than_or_equal_to: &Date.utc_today/0), + where: [present(:birth_date)], + message: "cannot be in the future" + + # Join date not in the future + validate compare(:join_date, less_than_or_equal_to: &Date.utc_today/0), + where: [present(:join_date)], + message: "cannot be in the future" + + + # Exit date not before join date + validate compare(:exit_date, greater_than: :join_date), + where: [present([:join_date, :exit_date])], + message: "cannot be before join date" + + + # Phone number format (only if set) + validate match(:phone_number, ~r/^\+?[0-9\- ]{6,20}$/), + where: [present(:phone_number)], + message: "is not a valid phone number" + + + # Postal code format (only if set) + validate match(:postal_code, ~r/^\d{5}$/), + where: [present(:postal_code)], + message: "must consist of 5 digits" + + + # Email validation with EctoCommons.EmailValidator + validate fn changeset, _ -> + email = Ash.Changeset.get_attribute(changeset, :email) + + changeset2 = + {%{}, %{email: :string}} + |> Ecto.Changeset.cast(%{email: email}, [:email]) + |> EctoCommons.EmailValidator.validate_email(:email, checks: [:html_input, :pow]) + + if changeset2.valid? do + :ok + else + {:error, field: :email, message: "is not a valid email"} + end + end + end + attributes do uuid_v7_primary_key :id + + attribute :first_name, :string do + allow_nil? false + constraints min_length: 1 + end + + attribute :last_name, :string do + allow_nil? false + constraints min_length: 1 + end + + attribute :email, :string do + allow_nil? false + constraints min_length: 5, max_length: 254 + end + + attribute :birth_date, :date do + allow_nil? true + end + + attribute :paid, :boolean do + allow_nil? true + end + + attribute :phone_number, :string do + allow_nil? true + end + + attribute :join_date, :date do + allow_nil? true + end + + attribute :exit_date, :date do + allow_nil? true + end + + attribute :notes, :string do + allow_nil? true + end + + attribute :city, :string do + allow_nil? true + end + + attribute :street, :string do + allow_nil? true + end + + attribute :house_number, :string do + allow_nil? true + end + + attribute :postal_code, :string do + allow_nil? true + end end relationships do diff --git a/lib/membership/property.ex b/lib/membership/property.ex index 0bd5eab..2c432a8 100644 --- a/lib/membership/property.ex +++ b/lib/membership/property.ex @@ -6,6 +6,10 @@ defmodule Mv.Membership.Property do postgres do table "properties" repo Mv.Repo + + references do + reference :member, on_delete: :delete + end end actions do diff --git a/lib/membership/property_type.ex b/lib/membership/property_type.ex index 8e42fa6..7444c13 100644 --- a/lib/membership/property_type.ex +++ b/lib/membership/property_type.ex @@ -21,7 +21,7 @@ defmodule Mv.Membership.PropertyType do attribute :value_type, :atom, constraints: [one_of: [:string, :integer, :boolean, :date, :email]], allow_nil?: false, - description: "Definies the datatype `Property.value` is interpreted as" + description: "Defines the datatype `Property.value` is interpreted as" attribute :description, :string, allow_nil?: true, public?: true diff --git a/lib/mv_web.ex b/lib/mv_web.ex index de7184d..4254449 100644 --- a/lib/mv_web.ex +++ b/lib/mv_web.ex @@ -55,6 +55,7 @@ defmodule MvWeb do use Phoenix.LiveView, layout: {MvWeb.Layouts, :app} + on_mount MvWeb.LiveHelpers unquote(html_helpers()) end end diff --git a/lib/mv_web/components/core_components.ex b/lib/mv_web/components/core_components.ex index 1e9b835..c35f1ce 100644 --- a/lib/mv_web/components/core_components.ex +++ b/lib/mv_web/components/core_components.ex @@ -673,4 +673,28 @@ defmodule MvWeb.CoreComponents do def translate_errors(errors, field) when is_list(errors) do for {^field, {msg, opts}} <- errors, do: translate_error({msg, opts}) end + + @doc """ + Renders a list of items with name and value pairs. + + ## Examples + <.generic_list items={[ + {item.name, item.value}, + {other.name, other.value} + ]} /> + """ + attr :items, :list, required: true, doc: "List of {name, value} tuples" + + def generic_list(assigns) do + ~H""" +
+
+
+
{name}
+
{value}
+
+
+
+ """ + end end diff --git a/lib/mv_web/components/layouts/app.html.heex b/lib/mv_web/components/layouts/app.html.heex index 3b3b607..54258db 100644 --- a/lib/mv_web/components/layouts/app.html.heex +++ b/lib/mv_web/components/layouts/app.html.heex @@ -9,6 +9,13 @@

+
+ + +
@elixirphoenix diff --git a/lib/mv_web/live_helpers.ex b/lib/mv_web/live_helpers.ex new file mode 100644 index 0000000..03d7d45 --- /dev/null +++ b/lib/mv_web/live_helpers.ex @@ -0,0 +1,7 @@ +defmodule MvWeb.LiveHelpers do + def on_mount(:default, _params, session, socket) do + locale = session["locale"] || "en" + Gettext.put_locale(locale) + {:cont, socket} + end +end diff --git a/lib/mv_web/locale_controller.ex b/lib/mv_web/locale_controller.ex new file mode 100644 index 0000000..3c8056f --- /dev/null +++ b/lib/mv_web/locale_controller.ex @@ -0,0 +1,18 @@ +defmodule MvWeb.LocaleController do + use MvWeb, :controller + + def set_locale(conn, %{"locale" => locale}) do + conn + |> put_session(:locale, locale) + |> redirect(to: get_referer(conn) || "/") + end + + defp get_referer(conn) do + conn.req_headers + |> Enum.find(fn {k, _v} -> k == "referer" end) + |> case do + {_, v} -> URI.parse(v).path + _ -> nil + end + end +end diff --git a/lib/mv_web/member_live/form_component.ex b/lib/mv_web/member_live/form_component.ex index 101cf6c..5535d1a 100644 --- a/lib/mv_web/member_live/form_component.ex +++ b/lib/mv_web/member_live/form_component.ex @@ -26,7 +26,9 @@ defmodule MvWeb.MemberLive.FormComponent do
<.header> {@title} - <:subtitle>Use this form to manage member records and their properties. + <:subtitle> + {gettext("Use this form to manage member records and their properties.")} + <.simple_form @@ -36,6 +38,21 @@ defmodule MvWeb.MemberLive.FormComponent do phx-change="validate" phx-submit="save" > + <.input field={@form[:first_name]} label={gettext("First Name")} required /> + <.input field={@form[:last_name]} label={gettext("Last Name")} required /> + <.input field={@form[:email]} label={gettext("Email")} required type="email" /> + <.input field={@form[:birth_date]} label={gettext("Birth Date")} type="date" /> + <.input field={@form[:paid]} label={gettext("Paid")} type="checkbox" /> + <.input field={@form[:phone_number]} label={gettext("Phone Number")} /> + <.input field={@form[:join_date]} label={gettext("Join Date")} type="date" /> + <.input field={@form[:exit_date]} label={gettext("Exit Date")} type="date" /> + <.input field={@form[:notes]} label={gettext("Notes")} /> + <.input field={@form[:city]} label={gettext("City")} /> + <.input field={@form[:street]} label={gettext("Street")} /> + <.input field={@form[:house_number]} label={gettext("House Number")} /> + <.input field={@form[:postal_code]} label={gettext("Postal Code")} /> + +

{gettext("Custom Properties")}

<.inputs_for :let={f_property} field={@form[:properties]}> <% type = Enum.find(@property_types, &(&1.id == f_property[:property_type_id].value)) %> <.inputs_for :let={value_form} field={f_property[:value]}> @@ -55,7 +72,7 @@ defmodule MvWeb.MemberLive.FormComponent do <:actions> - <.button phx-disable-with="Saving...">Save Member + <.button phx-disable-with={gettext("Saving...")}>{gettext("Save Member")}
@@ -80,9 +97,16 @@ defmodule MvWeb.MemberLive.FormComponent do {:ok, member} -> notify_parent({:saved, member}) + action = + case socket.assigns.form.source.type do + :create -> gettext("create") + :update -> gettext("update") + other -> to_string(other) + end + socket = socket - |> put_flash(:info, "Member #{socket.assigns.form.source.type}d successfully") + |> put_flash(:info, gettext("Member %{action} successfully", action: action)) |> push_patch(to: socket.assigns.patch) {:noreply, socket} diff --git a/lib/mv_web/member_live/index.ex b/lib/mv_web/member_live/index.ex index 4e37429..452ebab 100644 --- a/lib/mv_web/member_live/index.ex +++ b/lib/mv_web/member_live/index.ex @@ -5,10 +5,10 @@ defmodule MvWeb.MemberLive.Index do def render(assigns) do ~H""" <.header> - Listing Members + {gettext("Listing Members")} <:actions> <.link patch={~p"/members/new"}> - <.button>New Member + <.button>{gettext("New Member")} @@ -18,22 +18,27 @@ defmodule MvWeb.MemberLive.Index do rows={@streams.members} row_click={fn {_id, member} -> JS.navigate(~p"/members/#{member}") end} > - <:col :let={{_id, member}} label="Id">{member.id} + + <:col :let={{_id, member}} label={gettext("First Name")}>{member.first_name} + <:col :let={{_id, member}} label={gettext("Last Name")}>{member.last_name} + <:col :let={{_id, member}} label={gettext("Email")}>{member.email} + <:col :let={{_id, member}} label={gettext("City")}>{member.city} + <:col :let={{_id, member}} label={gettext("Join Date")}>{member.join_date} <:action :let={{_id, member}}>
- <.link navigate={~p"/members/#{member}"}>Show + <.link navigate={~p"/members/#{member}"}>{gettext("Show")}
- <.link patch={~p"/members/#{member}/edit"}>Edit + <.link patch={~p"/members/#{member}/edit"}>{gettext("Edit")} <:action :let={{id, member}}> <.link phx-click={JS.push("delete", value: %{id: member.id}) |> hide("##{id}")} - data-confirm="Are you sure?" + data-confirm={gettext("Are you sure?")} > - Delete + {gettext("Delete")} @@ -68,19 +73,19 @@ defmodule MvWeb.MemberLive.Index do defp apply_action(socket, :edit, %{"id" => id}) do socket - |> assign(:page_title, "Edit Member") + |> assign(:page_title, gettext("Edit Member")) |> assign(:member, Ash.get!(Mv.Membership.Member, id)) end defp apply_action(socket, :new, _params) do socket - |> assign(:page_title, "New Member") + |> assign(:page_title, gettext("New Member")) |> assign(:member, nil) end defp apply_action(socket, :index, _params) do socket - |> assign(:page_title, "Listing Members") + |> assign(:page_title, gettext("Listing Members")) |> assign(:member, nil) end diff --git a/lib/mv_web/member_live/show.ex b/lib/mv_web/member_live/show.ex index 47e0f92..612abd6 100644 --- a/lib/mv_web/member_live/show.ex +++ b/lib/mv_web/member_live/show.ex @@ -1,25 +1,55 @@ defmodule MvWeb.MemberLive.Show do use MvWeb, :live_view + import Ash.Query @impl true def render(assigns) do ~H""" <.header> - Member {@member.id} - <:subtitle>This is a member record from your database. + {@member.first_name} {@member.last_name} + <:subtitle>{gettext("This is a member record from your database.")} <:actions> <.link patch={~p"/members/#{@member}/show/edit"} phx-click={JS.push_focus()}> - <.button>Edit member + <.button>{gettext("Edit member")} <.list> - <:item title="Id">{@member.id} + <:item title={gettext("Id")}>{@member.id} + <:item title={gettext("First Name")}>{@member.first_name} + <:item title={gettext("Last Name")}>{@member.last_name} + <:item title={gettext("Email")}>{@member.email} + <:item title={gettext("Birth Date")}>{@member.birth_date} + <:item title={gettext("Paid")}> + {if @member.paid, do: gettext("Yes"), else: gettext("No")} + + <:item title={gettext("Phone Number")}>{@member.phone_number} + <:item title={gettext("Join Date")}>{@member.join_date} + <:item title={gettext("Exit Date")}>{@member.exit_date} + <:item title={gettext("Notes")}>{@member.notes} + <:item title={gettext("City")}>{@member.city} + <:item title={gettext("Street")}>{@member.street} + <:item title={gettext("House Number")}>{@member.house_number} + <:item title={gettext("Postal Code")}>{@member.postal_code} - <.back navigate={~p"/members"}>Back to members +

{gettext("Custom Properties")}

+ <.generic_list items={ + Enum.map(@member.properties, fn p -> + { + # name + p.property_type && p.property_type.name, + # value + case p.value do + %{value: v} -> v + v -> v + end + } + end) + } /> + <.back navigate={~p"/members"}>{gettext("Back to members")} <.modal :if={@live_action == :edit} @@ -46,12 +76,19 @@ defmodule MvWeb.MemberLive.Show do @impl true def handle_params(%{"id" => id}, _, socket) do + query = + Mv.Membership.Member + |> filter(id == ^id) + |> load(properties: [:property_type]) + + member = Ash.read_one!(query) + {:noreply, socket |> assign(:page_title, page_title(socket.assigns.live_action)) - |> assign(:member, Ash.get!(Mv.Membership.Member, id))} + |> assign(:member, member)} end - defp page_title(:show), do: "Show Member" - defp page_title(:edit), do: "Edit Member" + defp page_title(:show), do: gettext("Show Member") + defp page_title(:edit), do: gettext("Edit Member") end diff --git a/lib/mv_web/router.ex b/lib/mv_web/router.ex index 3e9bdca..f2cde75 100644 --- a/lib/mv_web/router.ex +++ b/lib/mv_web/router.ex @@ -8,6 +8,7 @@ defmodule MvWeb.Router do plug :put_root_layout, html: {MvWeb.Layouts, :root} plug :protect_from_forgery plug :put_secure_browser_headers + plug :set_locale end pipeline :api do @@ -35,6 +36,8 @@ defmodule MvWeb.Router do live "/properties/:id/edit", PropertyLive.Index, :edit live "/properties/:id", PropertyLive.Show, :show live "/properties/:id/show/edit", PropertyLive.Show, :edit + + post "/set_locale", LocaleController, :set_locale end # Other scopes may use custom stacks. @@ -68,4 +71,10 @@ defmodule MvWeb.Router do ash_admin "/" end end + + defp set_locale(conn, _opts) do + locale = get_session(conn, :locale) || "en" + Gettext.put_locale(MvWeb.Gettext, locale) + conn + end end diff --git a/mix.exs b/mix.exs index fd60217..2313ab4 100644 --- a/mix.exs +++ b/mix.exs @@ -35,7 +35,7 @@ defmodule Mv.MixProject do [ {:tidewave, "~> 0.1", only: [:dev]}, {:sourceror, "~> 1.8", only: [:dev, :test]}, - {:live_debugger, "~> 0.2", only: [:dev]}, + {:live_debugger, "~> 0.3", only: [:dev]}, {:ash_admin, "~> 0.13"}, {:ash_postgres, "~> 2.0"}, {:ash_phoenix, "~> 2.0"}, @@ -69,7 +69,8 @@ defmodule Mv.MixProject do {:bandit, "~> 1.5"}, {:mix_audit, "~> 2.1", only: [:dev, :test], runtime: false}, {:sobelow, "~> 0.14", only: [:dev, :test], runtime: false}, - {:credo, "~> 1.7", only: [:dev, :test], runtime: false} + {:credo, "~> 1.7", only: [:dev, :test], runtime: false}, + {:ecto_commons, "~> 0.3"} ] end diff --git a/mix.lock b/mix.lock index 7a7dff3..2fb6379 100644 --- a/mix.lock +++ b/mix.lock @@ -1,21 +1,23 @@ %{ - "ash": {:hex, :ash, "3.5.21", "389303c193962d67fd59da18a3557f5015fdfdaeddaa77150db539bc7203d1a1", [: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", "cb90005d1972e22d0d2ae514394e43e0d67cce18c4485595aa3d3e4bbf25260f"}, + "ash": {:hex, :ash, "3.5.24", "47bffb562c39482315d245ce22a381768b1bc16628ba974195630f3ca87d6218", [: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", "881e0decc5e75a0109ca545472b5c6ecb3b28893fec9eaf1866b8c35ddf78c16"}, "ash_admin": {:hex, :ash_admin, "0.13.10", "472a9d9367297db4337d0115c2b87dfa7bf08a67c955b266c3dae411c0b90016", [: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.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}], "hexpm", "9c190d13da7942652f2165ebf3f3716cfe81637669842bd41ed989fe03664463"}, - "ash_phoenix": {:hex, :ash_phoenix, "2.3.7", "70a74834f394508ac043bfc8d08487f32052b1762d78edf7c742091b14468258", [: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", "9cbc7b3275891e875017b112ea816ae0006e49993e58d411d666073276af2980"}, - "ash_postgres": {:hex, :ash_postgres, "2.6.8", "b6e662e63e4266552e324f4ad8df7c9f91b0652680473d940e0afb9f37957b58", [: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", [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", "790cd16858203eb58f5689a84bb863a4645fdba582f1ebfd56356013cbbb9b66"}, - "ash_sql": {:hex, :ash_sql, "0.2.82", "a4fe01ccd2c29ce43af50233e63cd1298735b68ee2c22a1cbb0baa5f31f78ab5", [:mix], [{:ash, "~> 3.5", [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", "a31f1065b72387b7a19d4e357f06904910b4c4fc8986209975786015f40cf795"}, + "ash_phoenix": {:hex, :ash_phoenix, "2.3.9", "684645f02725ca71625fcade6a4cc7c3a881a150762cdc532d03a32bde5a366d", [: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", "462ed487e62aa7de14d587c3fed64c9a3971e4132a9d0121033754c444c1400e"}, + "ash_postgres": {:hex, :ash_postgres, "2.6.9", "8312bbe1ec463036841f08861196595ea233eadc1cd4c8097a1701ff7b0e95ed", [: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", [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", "1eb0c258d7dbe594312b37697097745ebb396162f38cddde282e011c26eb834c"}, + "ash_sql": {:hex, :ash_sql, "0.2.83", "de8a9776186d1d1df54e265c1cf0c4e61c1d72be1297c9538f1a32eb9b84de55", [:mix], [{:ash, "~> 3.5", [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", "5c99192814177d589d2ba518968b97d1ec7af12446631e3e5538f7a617d7d289"}, "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"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "castore": {:hex, :castore, "1.0.14", "4582dd7d630b48cf5e1ca8d3d42494db51e406b7ba704e81fbd401866366896a", [:mix], [], "hexpm", "7bc1b65249d31701393edaaac18ec8398d8974d52c647b7904d01b964137b9f4"}, "circular_buffer": {:hex, :circular_buffer, "0.4.2", "b088989532af3d1733d35a08933ec9a232a56d0f06a078739f05fe24965d91d2", [:mix], [], "hexpm", "bc5f4ba112bce88aa4692427a8b23aa5baddf7c810d5dc3e7696f398194f104e"}, "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"}, - "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, + "db_connection": {:hex, :db_connection, "2.8.0", "64fd82cfa6d8e25ec6660cea73e92a4cbc6a18b31343910427b702838c4b33b2", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "008399dae5eee1bf5caa6e86d204dcb44242c82b1ed5e22c881f2c34da201b15"}, "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, "dns_cluster": {:hex, :dns_cluster, "0.2.0", "aa8eb46e3bd0326bd67b84790c561733b25c5ba2fe3c7e36f28e88f384ebcb33", [:mix], [], "hexpm", "ba6f1893411c69c01b9e8e8f772062535a4cf70f3f35bcc964a324078d8c8240"}, - "ecto": {:hex, :ecto, "3.13.0", "7528ef4f3a4cdcfebeb7eb6545806c8109529b385a69f701fc3d77b5b8bde6e7", [: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", "061f095f1cc097f71f743b500affc792d6869df22b1946a73ab5495eb9b4a280"}, - "ecto_sql": {:hex, :ecto_sql, "3.13.0", "a732428f38ce86612a2c34a1ea5d0a9642a5a71f044052007fd2f2e815707990", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5ce13085122a0871d93ea9ba1a886447d89c07f3b563e19e0b3dcdf201ed9fe9"}, + "ecto": {:hex, :ecto, "3.13.2", "7d0c0863f3fc8d71d17fc3ad3b9424beae13f02712ad84191a826c7169484f01", [: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", "669d9291370513ff56e7b7e7081b7af3283d02e046cf3d403053c557894a0b3e"}, + "ecto_commons": {:hex, :ecto_commons, "0.3.6", "7b1d9e59396cf8c8cbe5a26d50d03f9b6d0fe6c640210dd503622f276f1e59bb", [:mix], [{:burnex, "~> 3.0", [hex: :burnex, repo: "hexpm", optional: true]}, {:ecto, "~> 3.4", [hex: :ecto, repo: "hexpm", optional: false]}, {:ex_phone_number, "~> 0.2", [hex: :ex_phone_number, repo: "hexpm", optional: false]}, {:luhn, "~> 0.3.0", [hex: :luhn, repo: "hexpm", optional: false]}], "hexpm", "3f12981a1e398f206c5d2014e7b732b7ec91b110b9cb84875cb5b28fc75d7a0a"}, + "ecto_sql": {:hex, :ecto_sql, "3.13.2", "a07d2461d84107b3d037097c822ffdd36ed69d1cf7c0f70e12a3d1decf04e2e1", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "539274ab0ecf1a0078a6a72ef3465629e4d6018a3028095dc90f60a19c371717"}, "esbuild": {:hex, :esbuild, "0.10.0", "b0aa3388a1c23e727c5a3e7427c932d89ee791746b0081bbe56103e9ef3d291f", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "468489cda427b974a7cc9f03ace55368a83e1a7be12fba7e30969af78e5f8c70"}, "ets": {:hex, :ets, "0.9.0", "79c6a6c205436780486f72d84230c6cba2f8a9920456750ddd1e47389107d5fd", [:mix], [], "hexpm", "2861fdfb04bcaeff370f1a5904eec864f0a56dcfebe5921ea9aadf2a481c822b"}, + "ex_phone_number": {:hex, :ex_phone_number, "0.4.5", "2065cc48c3e9d1ed9821f50877c32f2f6898362cb990f44147ca217c5d1374ed", [:mix], [{:sweet_xml, "~> 0.7", [hex: :sweet_xml, repo: "hexpm", optional: false]}], "hexpm", "67163f8706f8cbfef1b1f4b9230c461f19786d0d79fd0b22cbeeefc6f0b99d4a"}, "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.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [: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", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"}, @@ -24,12 +26,13 @@ "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.7", "4e183afc59d89289e223c4282fd3e9bb39b82e28d0aa6d3369f70fbd3e21a243", [: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", "43b0a584dc84fd1320772c87047355b604ed2bcdd25392b17f7da8bdd09b61ac"}, + "igniter": {:hex, :igniter, "0.6.9", "99dd9ea7bcf2fe829617dac660069b3461183e4efbf303dd120fdef96923287d", [: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", "5fe407e10bc9416f7cd6af90d0409c8226ff2acacb9a7e7b9a097a66c8b5caef"}, "inflex": {:hex, :inflex, "2.1.0", "a365cf0821a9dacb65067abd95008ca1b0bb7dcdd85ae59965deef2aa062924c", [:mix], [], "hexpm", "14c17d05db4ee9b6d319b0bff1bdf22aa389a25398d1952c7a0b5f3d93162dd8"}, "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"}, "libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"}, - "live_debugger": {:hex, :live_debugger, "0.2.4", "2e0b02874ca562ba2d8cebb9e024c25c0ae9c1f4ee499135a70814e1dea6183e", [: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", "bfd0db143be54ccf2872f15bfd2209fbec1083d0b06b81b4cedeecb2fa9ac208"}, + "live_debugger": {:hex, :live_debugger, "0.3.0", "346837b808961d12eb4a6c12863dbb8d02b980720683d482f1ac8934821af404", [: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", "afd8c01346cb1dde8f9ff0185d3718182cd1a6c0262ce72f0f74ed0ad489b932"}, + "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"}, "mix_audit": {:hex, :mix_audit, "2.1.5", "c0f77cee6b4ef9d97e37772359a187a166c7a1e0e08b50edf5bf6959dfe5a016", [:make, :mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.11", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "87f9298e21da32f697af535475860dc1d3617a010e0b418d2ec6142bc8b42d69"}, @@ -37,7 +40,7 @@ "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.7.21", "14ca4f1071a5f65121217d6b57ac5712d1857e40a0833aff7a691b7870fc9a3b", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {: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", "336dce4f86cba56fed312a7d280bf2282c720abb6074bdb1b61ec8095bdd0bc9"}, - "phoenix_ecto": {:hex, :phoenix_ecto, "4.6.4", "dcf3483ab45bab4c15e3a47c34451392f64e433846b08469f5d16c2a4cd70052", [: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", "f5b8584c36ccc9b903948a696fc9b8b81102c79c7c0c751a9f00cdec55d5f2d7"}, + "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_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"}, @@ -45,26 +48,27 @@ "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"}, - "plug": {:hex, :plug, "1.18.0", "d78df36c41f7e798f2edf1f33e1727eae438e9dd5d809a9997c463a108244042", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "819f9e176d51e44dc38132e132fe0accaf6767eab7f0303431e404da8476cfa2"}, + "plug": {:hex, :plug, "1.18.1", "5067f26f7745b7e31bc3368bc1a2b818b9779faa959b49c934c17730efc911cf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "57a57db70df2b422b564437d2d33cf8d33cd16339c1edb190cd11b1a3a546cc2"}, "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, "postgrex": {:hex, :postgrex, "0.20.0", "363ed03ab4757f6bc47942eff7720640795eb557e1935951c1626f0d303a3aed", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "d36ef8b36f323d29505314f704e21a1a038e2dc387c6409ee0cd24144e187c0f"}, "reactor": {:hex, :reactor, "0.15.5", "341d9ee664d6141df6639f227692ee6adc8a493d04232dee79e8a4a88e6cef8a", [:mix], [{:igniter, "~> 0.4", [hex: :igniter, repo: "hexpm", optional: true]}, {:iterex, "~> 0.1", [hex: :iterex, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.11", [hex: :yaml_elixir, repo: "hexpm", optional: false]}, {:ymlr, "~> 5.0", [hex: :ymlr, repo: "hexpm", optional: false]}], "hexpm", "f9f440ecbdb0c41a832902a692608bd24be621fa7a602819d0dd12971d69f9aa"}, - "req": {:hex, :req, "0.5.10", "a3a063eab8b7510785a467f03d30a8d95f66f5c3d9495be3474b61459c54376c", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "8a604815743f8a2d3b5de0659fa3137fa4b1cffd636ecb69b30b2b9b2c2559be"}, + "req": {:hex, :req, "0.5.13", "6745796a5f4a9a11f23e0c8126411b27b728096a618e81e26cb11a1a25cfc6e2", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "d336a58dbc80ed13944bc857dd98387ba1b8f63592fc9f95b705a16389e987bd"}, "rewrite": {:hex, :rewrite, "1.1.2", "f5a5d10f5fed1491a6ff48e078d4585882695962ccc9e6c779bae025d1f92eda", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}, {:text_diff, "~> 0.1", [hex: :text_diff, repo: "hexpm", optional: false]}], "hexpm", "7f8b94b1e3528d0a47b3e8b7bfeca559d2948a65fa7418a9ad7d7712703d39d4"}, "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.66", "b7b47e76961c747f6128ad092c2109dbf742342dec533d3002c35207cb5f6b8e", [: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", "8f0b79839033ab816c2ae37aea29ded83e245a74240e2c931e2396531a9760d6"}, + "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"}, "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"}, - "swoosh": {:hex, :swoosh, "1.19.2", "b2325aa7cd2bcd63ba023fa07a73dfc4f80660a592d40912975a879966ed9b7b", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mua, "~> 0.2.3", [hex: :mua, repo: "hexpm", optional: true]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.5.10 or ~> 0.6 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cab7ef7c2c94c68fe21d3da26f6b86db118fdf4e7024ccb5842a4972c1056837"}, + "sweet_xml": {:hex, :sweet_xml, "0.7.5", "803a563113981aaac202a1dbd39771562d0ad31004ddbfc9b5090bdcd5605277", [:mix], [], "hexpm", "193b28a9b12891cae351d81a0cead165ffe67df1b73fe5866d10629f4faefb12"}, + "swoosh": {:hex, :swoosh, "1.19.3", "02ad4455939f502386e4e1443d4de94c514995fd0e51b3cafffd6bd270ffe81c", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mua, "~> 0.2.3", [hex: :mua, repo: "hexpm", optional: true]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.5.10 or ~> 0.6 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "04a10f8496786b744b84130e3510eb53ca51e769c39511b65023bdf4136b732f"}, "tailwind": {:hex, :tailwind, "0.3.1", "a89d2835c580748c7a975ad7dd3f2ea5e63216dc16d44f9df492fbd12c094bed", [:mix], [], "hexpm", "98a45febdf4a87bc26682e1171acdedd6317d0919953c353fcd1b4f9f4b676a2"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, "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.2.0", "ba82e333215aed9dd2096f93bd1d13ae89d249f82760fcada0850ba33bac154b", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7216e21a6c326eb9aa44328028c34e9fd348fb53667ca837be59d0aa2a0156e8"}, "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.1.8", "2bb2509ef884e40945c14a149bf3236dce7d557be0aa4e5c19d11ad9eb7ddf2f", [:mix], [{:circular_buffer, "~> 0.4", [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", "9228f3809f3e9fdd8c17f95dc35453e7fe5bb9ab3d409a09bfb99785025788b6"}, + "tidewave": {:hex, :tidewave, "0.1.10", "8cb6d25d7788b67b72aa0918219bfce956d5d37f02c6ce27602d38f88054979f", [:mix], [{:circular_buffer, "~> 0.4", [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", "a2f589f56b06c66354930ba74f3411635526c60c352dd88f6123415aed000525"}, "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"}, diff --git a/priv/gettext/de/LC_MESSAGES/default.po b/priv/gettext/de/LC_MESSAGES/default.po new file mode 100644 index 0000000..aa33cc3 --- /dev/null +++ b/priv/gettext/de/LC_MESSAGES/default.po @@ -0,0 +1,244 @@ +## `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" + +#: lib/mv_web/components/core_components.ex:482 +#, elixir-autogen, elixir-format +msgid "Actions" +msgstr "" + +#: lib/mv_web/member_live/index.ex:39 +#, elixir-autogen, elixir-format +msgid "Are you sure?" +msgstr "Bist du sicher?" + +#: lib/mv_web/components/core_components.ex:160 +#, elixir-autogen, elixir-format +msgid "Attempting to reconnect" +msgstr "Verbindung wird wiederhergestellt" + +#: lib/mv_web/member_live/form_component.ex:48 +#: lib/mv_web/member_live/index.ex:25 +#: lib/mv_web/member_live/show.ex:30 +#, elixir-autogen, elixir-format +msgid "City" +msgstr "Stadt" + +#: lib/mv_web/member_live/index.ex:41 +#, elixir-autogen, elixir-format +msgid "Delete" +msgstr "Löschen" + +#: lib/mv_web/member_live/index.ex:33 +#, elixir-autogen, elixir-format +msgid "Edit" +msgstr "Bearbeiten" + +#: lib/mv_web/member_live/index.ex:76 +#: lib/mv_web/member_live/show.ex:91 +#, elixir-autogen, elixir-format +msgid "Edit Member" +msgstr "Mitglied bearbeiten" + +#: lib/mv_web/member_live/form_component.ex:41 +#: lib/mv_web/member_live/index.ex:24 +#: lib/mv_web/member_live/show.ex:23 +#, elixir-autogen, elixir-format +msgid "Email" +msgstr "E-Mail" + +#: lib/mv_web/components/core_components.ex:151 +#, elixir-autogen, elixir-format +msgid "Error!" +msgstr "Fehler!" + +#: lib/mv_web/member_live/form_component.ex:39 +#: lib/mv_web/member_live/index.ex:22 +#: lib/mv_web/member_live/show.ex:21 +#, elixir-autogen, elixir-format +msgid "First Name" +msgstr "Vorname" + +#: lib/mv_web/components/core_components.ex:172 +#, elixir-autogen, elixir-format +msgid "Hang in there while we get back on track" +msgstr "Bitte warten, wir stellen die Verbindung wieder her." + +#: lib/mv_web/member_live/form_component.ex:45 +#: lib/mv_web/member_live/index.ex:26 +#: lib/mv_web/member_live/show.ex:27 +#, elixir-autogen, elixir-format +msgid "Join Date" +msgstr "Beitrittsdatum" + +#: lib/mv_web/member_live/form_component.ex:40 +#: lib/mv_web/member_live/index.ex:23 +#: lib/mv_web/member_live/show.ex:22 +#, elixir-autogen, elixir-format +msgid "Last Name" +msgstr "Nachname" + +#: lib/mv_web/member_live/index.ex:8 +#: lib/mv_web/member_live/index.ex:88 +#, elixir-autogen, elixir-format +msgid "Listing Members" +msgstr "Mitglieder" + +#: lib/mv_web/member_live/index.ex:11 +#: lib/mv_web/member_live/index.ex:82 +#, elixir-autogen, elixir-format +msgid "New Member" +msgstr "Neues Mitglied" + +#: lib/mv_web/member_live/index.ex:30 +#, elixir-autogen, elixir-format +msgid "Show" +msgstr "Anzeigen" + +#: lib/mv_web/components/core_components.ex:167 +#, elixir-autogen, elixir-format +msgid "Something went wrong!" +msgstr "Etwas ist schiefgelaufen!" + +#: lib/mv_web/components/core_components.ex:150 +#, elixir-autogen, elixir-format +msgid "Success!" +msgstr "Erfolg!" + +#: lib/mv_web/components/core_components.ex:155 +#, elixir-autogen, elixir-format +msgid "We can't find the internet" +msgstr "Keine Internetverbindung gefunden" + +#: lib/mv_web/components/core_components.ex:76 +#: lib/mv_web/components/core_components.ex:130 +#, elixir-autogen, elixir-format +msgid "close" +msgstr "schließen" + +#: lib/mv_web/member_live/form_component.ex:42 +#: lib/mv_web/member_live/show.ex:24 +#, elixir-autogen, elixir-format +msgid "Birth Date" +msgstr "Geburtsdatum" + +#: lib/mv_web/member_live/form_component.ex:53 +#: lib/mv_web/member_live/show.ex:36 +#, elixir-autogen, elixir-format +msgid "Custom Properties" +msgstr "Eigene Eigenschaften" + +#: lib/mv_web/member_live/form_component.ex:46 +#: lib/mv_web/member_live/show.ex:28 +#, elixir-autogen, elixir-format +msgid "Exit Date" +msgstr "Austrittsdatum" + +#: lib/mv_web/member_live/form_component.ex:50 +#: lib/mv_web/member_live/show.ex:32 +#, elixir-autogen, elixir-format +msgid "House Number" +msgstr "Hausnummer" + +#: lib/mv_web/member_live/form_component.ex:47 +#: lib/mv_web/member_live/show.ex:29 +#, elixir-autogen, elixir-format +msgid "Notes" +msgstr "Notizen" + +#: lib/mv_web/member_live/form_component.ex:43 +#: lib/mv_web/member_live/show.ex:25 +#, elixir-autogen, elixir-format +msgid "Paid" +msgstr "Bezahlt" + +#: lib/mv_web/member_live/form_component.ex:44 +#: lib/mv_web/member_live/show.ex:26 +#, elixir-autogen, elixir-format +msgid "Phone Number" +msgstr "Telefonnummer" + +#: lib/mv_web/member_live/form_component.ex:51 +#: lib/mv_web/member_live/show.ex:33 +#, elixir-autogen, elixir-format +msgid "Postal Code" +msgstr "Postleitzahl" + +#: lib/mv_web/member_live/form_component.ex:73 +#, elixir-autogen, elixir-format +msgid "Save Member" +msgstr "Mitglied speichern" + +#: lib/mv_web/member_live/form_component.ex:73 +#, elixir-autogen, elixir-format +msgid "Saving..." +msgstr "Speichern..." + +#: lib/mv_web/member_live/form_component.ex:49 +#: lib/mv_web/member_live/show.ex:31 +#, elixir-autogen, elixir-format +msgid "Street" +msgstr "Straße" + +#: lib/mv_web/member_live/form_component.ex:29 +#, elixir-autogen, elixir-format +msgid "Use this form to manage member records and their properties." +msgstr "Dieses Formular dient zur Verwaltung von Mitgliedern und deren Eigenschaften." + +#: lib/mv_web/member_live/show.ex:50 +#, elixir-autogen, elixir-format +msgid "Back to members" +msgstr "Zurück zur Mitgliederliste" + +#: lib/mv_web/member_live/show.ex:14 +#, elixir-autogen, elixir-format +msgid "Edit member" +msgstr "Mitglied bearbeiten" + +#: lib/mv_web/member_live/show.ex:20 +#, elixir-autogen, elixir-format +msgid "Id" +msgstr "ID" + +#: lib/mv_web/member_live/show.ex:25 +#, elixir-autogen, elixir-format +msgid "No" +msgstr "Nein" + +#: lib/mv_web/member_live/show.ex:90 +#, elixir-autogen, elixir-format, fuzzy +msgid "Show Member" +msgstr "Mitglied anzeigen" + +#: lib/mv_web/member_live/show.ex:10 +#, elixir-autogen, elixir-format +msgid "This is a member record from your database." +msgstr "Dies ist ein Mitglied aus deiner Datenbank." + +#: lib/mv_web/member_live/show.ex:25 +#, elixir-autogen, elixir-format +msgid "Yes" +msgstr "Ja" + +#: lib/mv_web/member_live/form_component.ex:107 +#, elixir-autogen, elixir-format +msgid "Member %{action} successfully" +msgstr "Mitglied %{action} erfolgreich" + +#: lib/mv_web/member_live/form_component.ex:100 +#, elixir-autogen, elixir-format +msgid "create" +msgstr "erstellt" + +#: lib/mv_web/member_live/form_component.ex:101 +#, elixir-autogen, elixir-format +msgid "update" +msgstr "aktualisiert" diff --git a/priv/gettext/de/LC_MESSAGES/errors.po b/priv/gettext/de/LC_MESSAGES/errors.po new file mode 100644 index 0000000..c0fba6d --- /dev/null +++ b/priv/gettext/de/LC_MESSAGES/errors.po @@ -0,0 +1,133 @@ +## `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" + +## From Ecto.Changeset.cast/4 +msgid "can't be blank" +msgstr "" + +## From Ecto.Changeset.unique_constraint/3 +msgid "has already been taken" +msgstr "" + +## From Ecto.Changeset.put_change/3 +msgid "is invalid" +msgstr "" + +## From Ecto.Changeset.validate_acceptance/3 +msgid "must be accepted" +msgstr "" + +## From Ecto.Changeset.validate_format/3 +msgid "has invalid format" +msgstr "" + +## From Ecto.Changeset.validate_subset/3 +msgid "has an invalid entry" +msgstr "" + +## From Ecto.Changeset.validate_exclusion/3 +msgid "is reserved" +msgstr "" + +## From Ecto.Changeset.validate_confirmation/3 +msgid "does not match confirmation" +msgstr "" + +## From Ecto.Changeset.no_assoc_constraint/3 +msgid "is still associated with this entry" +msgstr "" + +msgid "are still associated with this entry" +msgstr "" + +## From Ecto.Changeset.validate_length/3 +msgid "should have %{count} item(s)" +msgid_plural "should have %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be %{count} character(s)" +msgid_plural "should be %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be %{count} byte(s)" +msgid_plural "should be %{count} byte(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at least %{count} item(s)" +msgid_plural "should have at least %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at least %{count} character(s)" +msgid_plural "should be at least %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at least %{count} byte(s)" +msgid_plural "should be at least %{count} byte(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at most %{count} item(s)" +msgid_plural "should have at most %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at most %{count} character(s)" +msgid_plural "should be at most %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at most %{count} byte(s)" +msgid_plural "should be at most %{count} byte(s)" +msgstr[0] "" +msgstr[1] "" + +## From Ecto.Changeset.validate_number/3 +msgid "must be less than %{number}" +msgstr "" + +msgid "must be greater than %{number}" +msgstr "" + +msgid "must be less than or equal to %{number}" +msgstr "" + +msgid "must be greater than or equal to %{number}" +msgstr "" + +msgid "must be equal to %{number}" +msgstr "" + +msgid "is not a valid email" +msgstr "ist keine gültige E-Mail-Adresse" + +msgid "cannot be in the future" +msgstr "darf nicht in der Zukunft liegen" + +msgid "must be present" +msgstr "muss ausgefüllt sein" + +msgid "is not a valid phone number" +msgstr "ist keine gültige Telefonnummer" + +msgid "length must be greater than or equal to 5" +msgstr "Die Länge muss mindestens 5 Zeichen betragen" + +msgid "cannot be before join date" +msgstr "darf nicht vor dem Eintrittsdatum liegen" + +msgid "must consist of 5 digits" +msgstr "muss aus 5 Ziffern bestehen" diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot new file mode 100644 index 0000000..f5b79d7 --- /dev/null +++ b/priv/gettext/default.pot @@ -0,0 +1,245 @@ +## 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. +## +## Run "mix gettext.extract" to bring this file up to +## date. Leave "msgstr"s empty as changing them here has no +## effect: edit them in PO (.po) files instead. +# +msgid "" +msgstr "" + +#: lib/mv_web/components/core_components.ex:482 +#, elixir-autogen, elixir-format +msgid "Actions" +msgstr "" + +#: lib/mv_web/member_live/index.ex:39 +#, elixir-autogen, elixir-format +msgid "Are you sure?" +msgstr "" + +#: lib/mv_web/components/core_components.ex:160 +#, elixir-autogen, elixir-format +msgid "Attempting to reconnect" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:48 +#: lib/mv_web/member_live/index.ex:25 +#: lib/mv_web/member_live/show.ex:30 +#, elixir-autogen, elixir-format +msgid "City" +msgstr "" + +#: lib/mv_web/member_live/index.ex:41 +#, elixir-autogen, elixir-format +msgid "Delete" +msgstr "" + +#: lib/mv_web/member_live/index.ex:33 +#, elixir-autogen, elixir-format +msgid "Edit" +msgstr "" + +#: lib/mv_web/member_live/index.ex:76 +#: lib/mv_web/member_live/show.ex:91 +#, elixir-autogen, elixir-format +msgid "Edit Member" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:41 +#: lib/mv_web/member_live/index.ex:24 +#: lib/mv_web/member_live/show.ex:23 +#, elixir-autogen, elixir-format +msgid "Email" +msgstr "" + +#: lib/mv_web/components/core_components.ex:151 +#, elixir-autogen, elixir-format +msgid "Error!" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:39 +#: lib/mv_web/member_live/index.ex:22 +#: lib/mv_web/member_live/show.ex:21 +#, elixir-autogen, elixir-format +msgid "First Name" +msgstr "" + +#: lib/mv_web/components/core_components.ex:172 +#, elixir-autogen, elixir-format +msgid "Hang in there while we get back on track" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:45 +#: lib/mv_web/member_live/index.ex:26 +#: lib/mv_web/member_live/show.ex:27 +#, elixir-autogen, elixir-format +msgid "Join Date" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:40 +#: lib/mv_web/member_live/index.ex:23 +#: lib/mv_web/member_live/show.ex:22 +#, elixir-autogen, elixir-format +msgid "Last Name" +msgstr "" + +#: lib/mv_web/member_live/index.ex:8 +#: lib/mv_web/member_live/index.ex:88 +#, elixir-autogen, elixir-format +msgid "Listing Members" +msgstr "" + +#: lib/mv_web/member_live/index.ex:11 +#: lib/mv_web/member_live/index.ex:82 +#, elixir-autogen, elixir-format +msgid "New Member" +msgstr "" + +#: lib/mv_web/member_live/index.ex:30 +#, elixir-autogen, elixir-format +msgid "Show" +msgstr "" + +#: lib/mv_web/components/core_components.ex:167 +#, elixir-autogen, elixir-format +msgid "Something went wrong!" +msgstr "" + +#: lib/mv_web/components/core_components.ex:150 +#, elixir-autogen, elixir-format +msgid "Success!" +msgstr "" + +#: lib/mv_web/components/core_components.ex:155 +#, elixir-autogen, elixir-format +msgid "We can't find the internet" +msgstr "" + +#: lib/mv_web/components/core_components.ex:76 +#: lib/mv_web/components/core_components.ex:130 +#, elixir-autogen, elixir-format +msgid "close" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:42 +#: lib/mv_web/member_live/show.ex:24 +#, elixir-autogen, elixir-format +msgid "Birth Date" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:53 +#: lib/mv_web/member_live/show.ex:36 +#, elixir-autogen, elixir-format +msgid "Custom Properties" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:46 +#: lib/mv_web/member_live/show.ex:28 +#, elixir-autogen, elixir-format +msgid "Exit Date" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:50 +#: lib/mv_web/member_live/show.ex:32 +#, elixir-autogen, elixir-format +msgid "House Number" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:47 +#: lib/mv_web/member_live/show.ex:29 +#, elixir-autogen, elixir-format +msgid "Notes" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:43 +#: lib/mv_web/member_live/show.ex:25 +#, elixir-autogen, elixir-format +msgid "Paid" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:44 +#: lib/mv_web/member_live/show.ex:26 +#, elixir-autogen, elixir-format +msgid "Phone Number" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:51 +#: lib/mv_web/member_live/show.ex:33 +#, elixir-autogen, elixir-format +msgid "Postal Code" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:73 +#, elixir-autogen, elixir-format +msgid "Save Member" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:73 +#, elixir-autogen, elixir-format +msgid "Saving..." +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:49 +#: lib/mv_web/member_live/show.ex:31 +#, elixir-autogen, elixir-format +msgid "Street" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:29 +#, elixir-autogen, elixir-format +msgid "Use this form to manage member records and their properties." +msgstr "" + +#: lib/mv_web/member_live/show.ex:50 +#, elixir-autogen, elixir-format +msgid "Back to members" +msgstr "" + +#: lib/mv_web/member_live/show.ex:14 +#, elixir-autogen, elixir-format +msgid "Edit member" +msgstr "" + +#: lib/mv_web/member_live/show.ex:20 +#, elixir-autogen, elixir-format +msgid "Id" +msgstr "" + +#: lib/mv_web/member_live/show.ex:25 +#, elixir-autogen, elixir-format +msgid "No" +msgstr "" + +#: lib/mv_web/member_live/show.ex:90 +#, elixir-autogen, elixir-format +msgid "Show Member" +msgstr "" + +#: lib/mv_web/member_live/show.ex:10 +#, elixir-autogen, elixir-format +msgid "This is a member record from your database." +msgstr "" + +#: lib/mv_web/member_live/show.ex:25 +#, elixir-autogen, elixir-format +msgid "Yes" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:107 +#, elixir-autogen, elixir-format +msgid "Mitglied %{action} erfolgreich" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:100 +#, elixir-autogen, elixir-format +msgid "create" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:101 +#, elixir-autogen, elixir-format +msgid "update" +msgstr "" diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po new file mode 100644 index 0000000..6173d39 --- /dev/null +++ b/priv/gettext/en/LC_MESSAGES/default.po @@ -0,0 +1,245 @@ +## "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" + +#: lib/mv_web/components/core_components.ex:482 +#, elixir-autogen, elixir-format +msgid "Actions" +msgstr "" + +#: lib/mv_web/member_live/index.ex:39 +#, elixir-autogen, elixir-format +msgid "Are you sure?" +msgstr "" + +#: lib/mv_web/components/core_components.ex:160 +#, elixir-autogen, elixir-format +msgid "Attempting to reconnect" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:48 +#: lib/mv_web/member_live/index.ex:25 +#: lib/mv_web/member_live/show.ex:30 +#, elixir-autogen, elixir-format +msgid "City" +msgstr "" + +#: lib/mv_web/member_live/index.ex:41 +#, elixir-autogen, elixir-format +msgid "Delete" +msgstr "" + +#: lib/mv_web/member_live/index.ex:33 +#, elixir-autogen, elixir-format +msgid "Edit" +msgstr "" + +#: lib/mv_web/member_live/index.ex:76 +#: lib/mv_web/member_live/show.ex:91 +#, elixir-autogen, elixir-format +msgid "Edit Member" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:41 +#: lib/mv_web/member_live/index.ex:24 +#: lib/mv_web/member_live/show.ex:23 +#, elixir-autogen, elixir-format +msgid "Email" +msgstr "" + +#: lib/mv_web/components/core_components.ex:151 +#, elixir-autogen, elixir-format +msgid "Error!" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:39 +#: lib/mv_web/member_live/index.ex:22 +#: lib/mv_web/member_live/show.ex:21 +#, elixir-autogen, elixir-format +msgid "First Name" +msgstr "" + +#: lib/mv_web/components/core_components.ex:172 +#, elixir-autogen, elixir-format +msgid "Hang in there while we get back on track" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:45 +#: lib/mv_web/member_live/index.ex:26 +#: lib/mv_web/member_live/show.ex:27 +#, elixir-autogen, elixir-format +msgid "Join Date" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:40 +#: lib/mv_web/member_live/index.ex:23 +#: lib/mv_web/member_live/show.ex:22 +#, elixir-autogen, elixir-format +msgid "Last Name" +msgstr "" + +#: lib/mv_web/member_live/index.ex:8 +#: lib/mv_web/member_live/index.ex:88 +#, elixir-autogen, elixir-format +msgid "Listing Members" +msgstr "" + +#: lib/mv_web/member_live/index.ex:11 +#: lib/mv_web/member_live/index.ex:82 +#, elixir-autogen, elixir-format +msgid "New Member" +msgstr "" + +#: lib/mv_web/member_live/index.ex:30 +#, elixir-autogen, elixir-format +msgid "Show" +msgstr "" + +#: lib/mv_web/components/core_components.ex:167 +#, elixir-autogen, elixir-format +msgid "Something went wrong!" +msgstr "" + +#: lib/mv_web/components/core_components.ex:150 +#, elixir-autogen, elixir-format +msgid "Success!" +msgstr "" + +#: lib/mv_web/components/core_components.ex:155 +#, elixir-autogen, elixir-format +msgid "We can't find the internet" +msgstr "" + +#: lib/mv_web/components/core_components.ex:76 +#: lib/mv_web/components/core_components.ex:130 +#, elixir-autogen, elixir-format +msgid "close" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:42 +#: lib/mv_web/member_live/show.ex:24 +#, elixir-autogen, elixir-format +msgid "Birth Date" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:53 +#: lib/mv_web/member_live/show.ex:36 +#, elixir-autogen, elixir-format +msgid "Custom Properties" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:46 +#: lib/mv_web/member_live/show.ex:28 +#, elixir-autogen, elixir-format +msgid "Exit Date" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:50 +#: lib/mv_web/member_live/show.ex:32 +#, elixir-autogen, elixir-format +msgid "House Number" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:47 +#: lib/mv_web/member_live/show.ex:29 +#, elixir-autogen, elixir-format +msgid "Notes" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:43 +#: lib/mv_web/member_live/show.ex:25 +#, elixir-autogen, elixir-format +msgid "Paid" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:44 +#: lib/mv_web/member_live/show.ex:26 +#, elixir-autogen, elixir-format +msgid "Phone Number" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:51 +#: lib/mv_web/member_live/show.ex:33 +#, elixir-autogen, elixir-format +msgid "Postal Code" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:73 +#, elixir-autogen, elixir-format, fuzzy +msgid "Save Member" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:73 +#, elixir-autogen, elixir-format +msgid "Saving..." +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:49 +#: lib/mv_web/member_live/show.ex:31 +#, elixir-autogen, elixir-format +msgid "Street" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:29 +#, elixir-autogen, elixir-format +msgid "Use this form to manage member records and their properties." +msgstr "" + +#: lib/mv_web/member_live/show.ex:50 +#, elixir-autogen, elixir-format +msgid "Back to members" +msgstr "" + +#: lib/mv_web/member_live/show.ex:14 +#, elixir-autogen, elixir-format, fuzzy +msgid "Edit member" +msgstr "" + +#: lib/mv_web/member_live/show.ex:20 +#, elixir-autogen, elixir-format +msgid "Id" +msgstr "" + +#: lib/mv_web/member_live/show.ex:25 +#, elixir-autogen, elixir-format +msgid "No" +msgstr "" + +#: lib/mv_web/member_live/show.ex:90 +#, elixir-autogen, elixir-format, fuzzy +msgid "Show Member" +msgstr "" + +#: lib/mv_web/member_live/show.ex:10 +#, elixir-autogen, elixir-format +msgid "This is a member record from your database." +msgstr "" + +#: lib/mv_web/member_live/show.ex:25 +#, elixir-autogen, elixir-format +msgid "Yes" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:107 +#, elixir-autogen, elixir-format +msgid "Member %{action} successfully" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:100 +#, elixir-autogen, elixir-format +msgid "create" +msgstr "" + +#: lib/mv_web/member_live/form_component.ex:101 +#, elixir-autogen, elixir-format +msgid "update" +msgstr "" diff --git a/priv/gettext/en/LC_MESSAGES/errors.po b/priv/gettext/en/LC_MESSAGES/errors.po index 844c4f5..60c1037 100644 --- a/priv/gettext/en/LC_MESSAGES/errors.po +++ b/priv/gettext/en/LC_MESSAGES/errors.po @@ -110,3 +110,12 @@ msgstr "" msgid "must be equal to %{number}" msgstr "" + +msgid "length must be greater than or equal to 5" +msgstr "length must be greater than or equal to 5" + +msgid "cannot be before join date" +msgstr "cannot be before join date" + +msgid "must consist of 5 digits" +msgstr "must consist of 5 digits" diff --git a/priv/repo/migrations/20250617090641_member_fields.exs b/priv/repo/migrations/20250617090641_member_fields.exs new file mode 100644 index 0000000..36a80eb --- /dev/null +++ b/priv/repo/migrations/20250617090641_member_fields.exs @@ -0,0 +1,45 @@ +defmodule Mv.Repo.Migrations.MemberFields 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 :first_name, :text, null: false + add :last_name, :text, null: false + add :email, :text, null: false + add :birth_date, :date + add :paid, :boolean + add :phone_number, :text + add :join_date, :date + add :exit_date, :date + add :notes, :text + add :city, :text + add :street, :text + add :house_number, :text + add :postal_code, :text + end + end + + def down do + alter table(:members) do + remove :postal_code + remove :house_number + remove :street + remove :city + remove :notes + remove :exit_date + remove :join_date + remove :phone_number + remove :paid + remove :birth_date + remove :email + remove :last_name + remove :first_name + end + end +end diff --git a/priv/repo/migrations/20250617132424_member_delete.exs b/priv/repo/migrations/20250617132424_member_delete.exs new file mode 100644 index 0000000..f0f539a --- /dev/null +++ b/priv/repo/migrations/20250617132424_member_delete.exs @@ -0,0 +1,38 @@ +defmodule Mv.Repo.Migrations.MemberDelete 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 + drop constraint(:properties, "properties_member_id_fkey") + + alter table(:properties) do + modify :member_id, + references(:members, + column: :id, + name: "properties_member_id_fkey", + type: :uuid, + prefix: "public", + on_delete: :delete_all + ) + end + end + + def down do + drop constraint(:properties, "properties_member_id_fkey") + + alter table(:properties) do + modify :member_id, + references(:members, + column: :id, + name: "properties_member_id_fkey", + type: :uuid, + prefix: "public" + ) + end + end +end diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 39327d0..1497096 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -7,37 +7,30 @@ alias Mv.Membership for attrs <- [ %{ - name: "Vorname", + name: "String Field", value_type: :string, - description: "Vorname des Mitglieds", + description: "Example for a field of type string", immutable: true, required: true }, %{ - name: "Nachname", - value_type: :string, - description: "Nachname des Mitglieds", - immutable: true, - required: true - }, - %{ - name: "Geburtsdatum", + name: "Date Field", value_type: :date, - description: "Geburtsdatum des Mitglieds", + description: "Example for a field of type date", immutable: true, required: true }, %{ - name: "Bezahlt", + name: "Boolean Field", value_type: :boolean, - description: "Status des Mitgliedsbeitrages des Mitglieds", + description: "Example for a field of type boolean", immutable: true, required: true }, %{ - name: "Email", + name: "Email Field", value_type: :email, - description: "Email-Adresse des Mitglieds", + description: "Example for a field of type email", immutable: true, required: true } diff --git a/priv/resource_snapshots/repo/members/20250617090641.json b/priv/resource_snapshots/repo/members/20250617090641.json new file mode 100644 index 0000000..9e2d991 --- /dev/null +++ b/priv/resource_snapshots/repo/members/20250617090641.json @@ -0,0 +1,187 @@ +{ + "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" + } + ], + "base_filter": null, + "check_constraints": [], + "custom_indexes": [], + "custom_statements": [], + "has_create_action": true, + "hash": "CF80317E7EE409618E08458B10EE122FF605640DDA8CD6000B433F1979614F5D", + "identities": [], + "multitenancy": { + "attribute": null, + "global": null, + "strategy": null + }, + "repo": "Elixir.Mv.Repo", + "schema": null, + "table": "members" +} \ No newline at end of file diff --git a/priv/resource_snapshots/repo/properties/20250617132424.json b/priv/resource_snapshots/repo/properties/20250617132424.json new file mode 100644 index 0000000..49e3b48 --- /dev/null +++ b/priv/resource_snapshots/repo/properties/20250617132424.json @@ -0,0 +1,105 @@ +{ + "attributes": [ + { + "allow_nil?": false, + "default": "fragment(\"gen_random_uuid()\")", + "generated?": false, + "precision": null, + "primary_key?": true, + "references": null, + "scale": null, + "size": null, + "source": "id", + "type": "uuid" + }, + { + "allow_nil?": true, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": null, + "scale": null, + "size": null, + "source": "value", + "type": "map" + }, + { + "allow_nil?": true, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": { + "deferrable": false, + "destination_attribute": "id", + "destination_attribute_default": null, + "destination_attribute_generated": null, + "index?": false, + "match_type": null, + "match_with": null, + "multitenancy": { + "attribute": null, + "global": null, + "strategy": null + }, + "name": "properties_member_id_fkey", + "on_delete": "delete", + "on_update": null, + "primary_key?": true, + "schema": "public", + "table": "members" + }, + "scale": null, + "size": null, + "source": "member_id", + "type": "uuid" + }, + { + "allow_nil?": true, + "default": "nil", + "generated?": false, + "precision": null, + "primary_key?": false, + "references": { + "deferrable": false, + "destination_attribute": "id", + "destination_attribute_default": null, + "destination_attribute_generated": null, + "index?": false, + "match_type": null, + "match_with": null, + "multitenancy": { + "attribute": null, + "global": null, + "strategy": null + }, + "name": "properties_property_type_id_fkey", + "on_delete": null, + "on_update": null, + "primary_key?": true, + "schema": "public", + "table": "property_types" + }, + "scale": null, + "size": null, + "source": "property_type_id", + "type": "uuid" + } + ], + "base_filter": null, + "check_constraints": [], + "custom_indexes": [], + "custom_statements": [], + "has_create_action": true, + "hash": "4F17BE0106435A1D75D46A3ABDE6A3DA20FC9B1C43D101B6C310009279DD7CBA", + "identities": [], + "multitenancy": { + "attribute": null, + "global": null, + "strategy": null + }, + "repo": "Elixir.Mv.Repo", + "schema": null, + "table": "properties" +} \ No newline at end of file diff --git a/test/membership/member_test.exs b/test/membership/member_test.exs new file mode 100644 index 0000000..7015d34 --- /dev/null +++ b/test/membership/member_test.exs @@ -0,0 +1,110 @@ +defmodule Mv.Membership.MemberTest do + use Mv.DataCase, async: false + alias Mv.Membership + + describe "Fields and Validations" do + @valid_attrs %{ + first_name: "John", + last_name: "Doe", + birth_date: ~D[1990-01-01], + paid: true, + email: "john@example.com", + phone_number: "+49123456789", + join_date: ~D[2020-01-01], + exit_date: nil, + notes: "Test note", + city: "Berlin", + street: "Main Street", + house_number: "1A", + postal_code: "12345" + } + + test "First name is required and must not be empty" do + attrs = Map.put(@valid_attrs, :first_name, "") + assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) + assert error_message(errors, :first_name) =~ "must be present" + end + + test "Last name is required and must not be empty" do + attrs = Map.put(@valid_attrs, :last_name, "") + assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) + assert error_message(errors, :last_name) =~ "must be present" + end + + test "Email is required" do + attrs = Map.put(@valid_attrs, :email, "") + assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) + assert error_message(errors, :email) =~ "must be present" + end + + test "Email must be valid" do + attrs = Map.put(@valid_attrs, :email, "test@") + assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) + assert error_message(errors, :email) =~ "is not a valid email" + end + + test "Birth date is optional but must not be in the future" do + attrs = Map.put(@valid_attrs, :birth_date, Date.utc_today() |> Date.add(1)) + assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) + assert error_message(errors, :birth_date) =~ "cannot be in the future" + end + + test "Paid is optional but must be boolean if specified" do + attrs = Map.put(@valid_attrs, :paid, nil) + attrs2 = Map.put(@valid_attrs, :paid, "yes") + assert {:ok, _member} = Membership.create_member(Map.delete(attrs, :paid)) + assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs2) + assert error_message(errors, :paid) =~ "is invalid" + end + + test "Phone number is optional but must have a valid format if specified" do + attrs = Map.put(@valid_attrs, :phone_number, "abc") + assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) + assert error_message(errors, :phone_number) =~ "is not a valid phone number" + attrs2 = Map.delete(@valid_attrs, :phone_number) + assert {:ok, _member} = Membership.create_member(attrs2) + end + + test "Join date is optional but must not be in the future" do + attrs = Map.put(@valid_attrs, :join_date, Date.utc_today() |> Date.add(1)) + assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) + assert error_message(errors, :join_date) =~ "cannot be in the future" + attrs2 = Map.delete(@valid_attrs, :join_date) + assert {:ok, _member} = Membership.create_member(attrs2) + end + + test "Exit date is optional but must not be before join date if both are specified" do + attrs = Map.put(@valid_attrs, :exit_date, ~D[2010-01-01]) + assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) + assert error_message(errors, :exit_date) =~ "cannot be before join date" + attrs2 = Map.delete(@valid_attrs, :exit_date) + assert {:ok, _member} = Membership.create_member(attrs2) + end + + test "Notes is optional" do + attrs = Map.delete(@valid_attrs, :notes) + assert {:ok, _member} = Membership.create_member(attrs) + end + + test "City, street, house number are optional" do + attrs = @valid_attrs |> Map.drop([:city, :street, :house_number]) + assert {:ok, _member} = Membership.create_member(attrs) + end + + test "Postal code is optional but must have 5 digits if specified" do + attrs = Map.put(@valid_attrs, :postal_code, "1234") + assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) + assert error_message(errors, :postal_code) =~ "must consist of 5 digits" + attrs2 = Map.delete(@valid_attrs, :postal_code) + assert {:ok, _member} = Membership.create_member(attrs2) + end + end + + # Helper function for error evaluation + defp error_message(errors, field) do + errors + |> Enum.filter(fn err -> Map.get(err, :field) == field end) + |> Enum.map(&Map.get(&1, :message, "")) + |> List.first() + end +end diff --git a/test/mv_web/locale_test.exs b/test/mv_web/locale_test.exs new file mode 100644 index 0000000..1cc6693 --- /dev/null +++ b/test/mv_web/locale_test.exs @@ -0,0 +1,14 @@ +defmodule MvWeb.LocaleTest do + use MvWeb.ConnCase, async: true + import Phoenix.ConnTest + + test "language switch via form sets the locale to English in the session" do + conn = post(build_conn(), "/set_locale", %{"locale" => "en"}) + assert get_session(conn, :locale) == "en" + end + + test "language switch via form sets the locale to German in the session" do + conn = post(build_conn(), "/set_locale", %{"locale" => "de"}) + assert get_session(conn, :locale) == "de" + end +end diff --git a/test/mv_web/member_live/index_test.exs b/test/mv_web/member_live/index_test.exs new file mode 100644 index 0000000..b5a5968 --- /dev/null +++ b/test/mv_web/member_live/index_test.exs @@ -0,0 +1,60 @@ +defmodule MvWeb.MemberLive.IndexTest do + use MvWeb.ConnCase, async: true + import Phoenix.LiveViewTest + + test "shows translated title in German", %{conn: conn} do + conn = Plug.Test.init_test_session(conn, locale: "de") + {:ok, _view, html} = live(conn, "/members") + # Expected German title + assert html =~ "Mitglieder" + end + + test "shows translated title in English", %{conn: conn} do + Gettext.put_locale(MvWeb.Gettext, "en") + {:ok, _view, html} = live(conn, "/members") + # Expected English title + assert html =~ "Members" + end + + test "shows translated button text in German", %{conn: conn} do + conn = Plug.Test.init_test_session(conn, locale: "de") + {:ok, _view, html} = live(conn, "/members/new") + assert html =~ "Speichern" + end + + test "shows translated button text in English", %{conn: conn} do + Gettext.put_locale(MvWeb.Gettext, "en") + {:ok, _view, html} = live(conn, "/members/new") + assert html =~ "Save" + end + + test "shows translated flash message after creating a member in German", %{conn: conn} do + conn = Plug.Test.init_test_session(conn, locale: "de") + {:ok, view, _html} = live(conn, "/members") + view |> element("a", "Neues Mitglied") |> render_click() + + form_data = %{ + "member[first_name]" => "Max", + "member[last_name]" => "Mustermann", + "member[email]" => "max@example.com" + } + + view |> form("#member-form", form_data) |> render_submit() + assert has_element?(view, "#flash-group", "Mitglied erstellt erfolgreich") + end + + test "shows translated flash message after creating a member in English", %{conn: conn} do + conn = Plug.Test.init_test_session(conn, locale: "en") + {:ok, view, _html} = live(conn, "/members") + view |> element("a", "New Member") |> render_click() + + form_data = %{ + "member[first_name]" => "Max", + "member[last_name]" => "Mustermann", + "member[email]" => "max@example.com" + } + + view |> form("#member-form", form_data) |> render_submit() + assert has_element?(view, "#flash-group", "Member create successfully") + end +end