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. 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