defmodule MvWeb.ImportLive.Components do @moduledoc """ Function components for the Import LiveView: import form, progress, results, custom fields notice, and template links. Keeps the main LiveView focused on mount/handle_event/handle_info and glue code. """ use Phoenix.Component use Gettext, backend: MvWeb.Gettext import MvWeb.CoreComponents use Phoenix.VerifiedRoutes, endpoint: MvWeb.Endpoint, router: MvWeb.Router, statics: MvWeb.static_paths() @doc """ Renders the info box explaining that data fields must exist before import and linking to Manage Member Data (custom fields). """ def custom_fields_notice(assigns) do ~H"""
<.icon name="hero-information-circle" class="size-5" aria-hidden="true" />

{gettext( "Use the data field name as the CSV column header in your file. Data fields must exist in Mila before importing, because unknown data field columns will be ignored with a warning. Groups and membership fees are not supported for import." )}

<.link href={~p"/settings#custom_fields"} class="link" data-testid="custom-fields-link" > {gettext("Manage Member Data")}

""" end @doc """ Renders download links for English and German CSV templates. """ def template_links(assigns) do ~H"""

{gettext("Download CSV templates:")}

""" end @doc """ Renders the CSV file upload form and Start Import button. """ def import_form(assigns) do ~H""" <.form id="csv-upload-form" for={%{}} multipart={true} phx-change="validate_csv_upload" phx-submit="start_import" data-testid="csv-upload-form" >
<.live_file_input upload={@uploads.csv_file} id="csv_file" class="file-input file-input-bordered" aria-describedby="csv_file_help" />

{gettext("CSV files only, maximum %{size} MB", size: @csv_import_max_file_size_mb)}

<.button type="submit" phx-disable-with={gettext("Starting import...")} variant="primary" disabled={import_button_disabled?(@import_status, @uploads.csv_file.entries)} data-testid="start-import-button" > {gettext("Start Import")} """ end @doc """ Renders import progress text and, when done or aborted, the import results section. """ def import_progress(assigns) do ~H""" <%= if @import_progress do %>
<%= if @import_progress.status == :running do %>

{gettext("Processing chunk %{current} of %{total}...", current: @import_progress.current_chunk, total: @import_progress.total_chunks )}

<% end %> <%= if @import_progress.status == :done or @import_status == :error do %> <.import_results {assigns} /> <% end %>
<% end %> """ end @doc """ Renders import results summary, error list, and warnings. Shown when import is done or aborted (:error); heading reflects state. """ def import_results(assigns) do ~H"""

<%= if @import_status == :error do %> {gettext("Import aborted")} <% else %> {gettext("Import Results")} <% end %>

{gettext("Summary")}

<.icon name="hero-check-circle" class="size-4 inline mr-1" aria-hidden="true" /> {gettext("Successfully inserted: %{count} member(s)", count: @import_progress.inserted )}

<%= if @import_progress.failed > 0 do %>

<.icon name="hero-exclamation-circle" class="size-4 inline mr-1" aria-hidden="true" /> {gettext("Failed: %{count} row(s)", count: @import_progress.failed)}

<% end %> <%= if @import_progress.errors_truncated? do %>

<.icon name="hero-information-circle" class="size-4 inline mr-1" aria-hidden="true" /> {gettext("Error list truncated to %{count} entries", count: @max_errors)}

<% end %>
<%= if length(@import_progress.errors) > 0 do %>

<.icon name="hero-exclamation-circle" class="size-4 inline mr-1" aria-hidden="true" /> {gettext("Errors")}

    <%= for error <- @import_progress.errors do %>
  • {gettext("Line %{line}: %{message}", line: error.csv_line_number || "?", message: error.message || gettext("Unknown error") )} <%= if error.field do %> {gettext(" (Field: %{field})", field: error.field)} <% end %>
  • <% end %>
<% end %> <%= if length(@import_progress.warnings) > 0 do %> <% end %>
""" end @doc """ Returns whether the Start Import button should be disabled. """ @spec import_button_disabled?(:idle | :running | :done | :error, [map()]) :: boolean() def import_button_disabled?(:running, _entries), do: true def import_button_disabled?(_status, []), do: true def import_button_disabled?(_status, [entry | _]) when not entry.done?, do: true def import_button_disabled?(_status, _entries), do: false end