Merge branch 'main' into feat/421_accessibility
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
carla 2026-02-26 08:49:55 +01:00
commit 73382c2c3f
49 changed files with 3415 additions and 1950 deletions

View file

@ -24,11 +24,13 @@ defmodule MvWeb.CustomFieldLive.FormComponent do
<div class="flex items-center gap-4 mb-4">
<.button
type="button"
variant="neutral"
phx-click="cancel"
phx-target={@myself}
aria-label={gettext("Back to settings")}
>
<.icon name="hero-arrow-left" class="w-4 h-4" />
<.icon name="hero-arrow-left" class="size-4" />
{gettext("Back")}
</.button>
<h3 class="card-title">
{if @custom_field, do: gettext("Edit Data Field"), else: gettext("New Data Field")}
@ -96,8 +98,35 @@ defmodule MvWeb.CustomFieldLive.FormComponent do
label={gettext("Show in overview")}
/>
<%= if @custom_field do %>
<%!-- Danger zone: canonical pattern (same as member form) --%>
<section class="mt-8 mb-6" aria-labelledby="danger-zone-heading">
<h2 id="danger-zone-heading" class="text-lg font-semibold mb-3 text-error">
{gettext("Danger zone")}
</h2>
<div class="border border-base-300 rounded-lg p-4 bg-base-100">
<p class="text-base-content/70 mb-4">
{gettext(
"Deleting this data field cannot be undone. All datafield values for this field will be permanently removed."
)}
</p>
<.button
type="button"
variant="danger"
phx-click="request_delete"
phx-target={@myself}
data-testid="custom-field-delete"
aria-label={gettext("Delete data field %{name}", name: @custom_field.name)}
>
<.icon name="hero-trash" class="size-4" />
{gettext("Delete data field")}
</.button>
</div>
</section>
<% end %>
<div class="justify-end mt-4 card-actions">
<.button type="button" phx-click="cancel" phx-target={@myself}>
<.button type="button" variant="neutral" phx-click="cancel" phx-target={@myself}>
{gettext("Cancel")}
</.button>
<.button phx-disable-with={gettext("Saving...")} variant="primary">
@ -168,6 +197,15 @@ defmodule MvWeb.CustomFieldLive.FormComponent do
{:noreply, socket}
end
@impl true
def handle_event("request_delete", _params, socket) do
if custom_field = socket.assigns[:custom_field] do
send(self(), {:open_delete_modal_for, custom_field})
end
{:noreply, socket}
end
defp assign_form(%{assigns: %{custom_field: custom_field}} = socket) do
form =
if custom_field do

View file

@ -59,6 +59,7 @@ defmodule MvWeb.CustomFieldLive.IndexComponent do
JS.push("edit_custom_field", value: %{id: custom_field.id}, target: @myself)
end
}
row_tooltip={gettext("Click to edit datafield")}
>
<:col :let={{_id, custom_field}} label={gettext("Name")}>{custom_field.name}</:col>
@ -95,22 +96,6 @@ defmodule MvWeb.CustomFieldLive.IndexComponent do
{gettext("No")}
</.badge>
</:col>
<:action :let={{_id, custom_field}}>
<.link phx-click={
JS.push("edit_custom_field", value: %{id: custom_field.id}, target: @myself)
}>
{gettext("Edit")}
</.link>
</:action>
<:action :let={{_id, custom_field}}>
<.link phx-click={
JS.push("prepare_delete", value: %{id: custom_field.id}, target: @myself)
}>
{gettext("Delete")}
</.link>
</:action>
</.table>
</div>
@ -164,17 +149,17 @@ defmodule MvWeb.CustomFieldLive.IndexComponent do
</div>
<div class="modal-action">
<button phx-click="cancel_delete" phx-target={@myself} class="btn">
<.button variant="neutral" phx-click="cancel_delete" phx-target={@myself}>
{gettext("Cancel")}
</button>
<button
</.button>
<.button
variant="danger"
phx-click="confirm_delete"
phx-target={@myself}
class="btn btn-error"
disabled={@slug_confirmation != @custom_field_to_delete.slug}
>
{gettext("Delete Custom Field and All Values")}
</button>
{gettext("Delete Datafields and All Values")}
</.button>
</div>
</div>
</dialog>
@ -222,16 +207,38 @@ defmodule MvWeb.CustomFieldLive.IndexComponent do
# Get actor from assigns or fall back to socket assigns
actor = Map.get(assigns, :actor, socket.assigns[:actor])
{:ok,
socket
|> assign(assigns)
|> assign_new(:show_form, fn -> false end)
|> assign_new(:form_id, fn -> "custom-field-form-new" end)
|> assign_new(:editing_custom_field, fn -> nil end)
|> assign_new(:show_delete_modal, fn -> false end)
|> assign_new(:custom_field_to_delete, fn -> nil end)
|> assign_new(:slug_confirmation, fn -> "" end)
|> stream(:custom_fields, stream_custom_fields(actor, self()), reset: true)}
socket =
socket
|> assign(assigns)
|> assign_new(:show_form, fn -> false end)
|> assign_new(:form_id, fn -> "custom-field-form-new" end)
|> assign_new(:editing_custom_field, fn -> nil end)
|> assign_new(:show_delete_modal, fn -> false end)
|> assign_new(:custom_field_to_delete, fn -> nil end)
|> assign_new(:slug_confirmation, fn -> "" end)
|> stream(:custom_fields, stream_custom_fields(actor, self()), reset: true)
# Open delete modal when requested from form (e.g. Danger zone in FormComponent)
socket =
case Map.get(assigns, :open_delete_for_id) do
nil ->
socket
id ->
custom_field =
Ash.get!(Mv.Membership.CustomField, id,
load: [:assigned_members_count],
actor: actor
)
socket
|> assign(:show_delete_modal, true)
|> assign(:custom_field_to_delete, custom_field)
|> assign(:slug_confirmation, "")
|> assign(:open_delete_for_id, nil)
end
{:ok, socket}
end
@impl true