defmodule MicrowavepropWeb.SubmitLive.PreviewComponent do @moduledoc false use MicrowavepropWeb, :html alias Phoenix.LiveView.Rendered @doc false @spec csv_preview(map()) :: Rendered.t() def csv_preview(assigns) do assigns = assign(assigns, valid_count: length(assigns.preview.valid), invalid_count: length(assigns.preview.invalid), duplicate_count: length(assigns.preview.duplicates), refinement_count: length(assigns.preview.refinements || []), valid_sample: Enum.take(assigns.preview.valid, 20), refinement_sample: Enum.take(assigns.preview.refinements || [], 50) ) ~H"""

Review before submitting

Processed {@preview.total_rows} {if @preview.total_rows == 1, do: "row", else: "rows"}. Nothing has been inserted yet — confirm below to import.

<.summary_card tone="success" label="Valid" value={@valid_count} hint="Will be inserted" /> <.summary_card tone="info" label="Refinements" value={@refinement_count} hint="Will update existing contacts" /> <.summary_card tone="warning" label="Duplicates" value={@duplicate_count} hint="Match existing or earlier rows" /> <.summary_card tone="error" label="Invalid" value={@invalid_count} hint="Skipped — see errors" />
0}>

Invalid rows ({@invalid_count})

Row Errors
Row {row.row_num}
{msg}

100} class="text-xs opacity-60 mt-1"> Showing first 100 of {@invalid_count} invalid rows.

0}>

Refinements ({@refinement_count})

These rows match an existing contact but add more precise data. Confirming will update the existing contact in place.

Row Station 1 Station 2 Band Changes
Row {row.row_num} {row.attrs["station1"]} {row.attrs["grid1"]} {row.attrs["station2"]} {row.attrs["grid2"]} {row.attrs["band"]}
{field}: {value}

length(@refinement_sample)} class="text-xs opacity-60 mt-1"> Showing first {length(@refinement_sample)} of {@refinement_count} refinements.

0}>

Duplicates ({@duplicate_count})

Same two callsigns at the same grids on the same band within an hour.

Row Station 1 Station 2 Band Timestamp Source
Row {row.row_num} {row.attrs["station1"]} {row.attrs["grid1"]} {row.attrs["station2"]} {row.attrs["grid2"]} {row.attrs["band"]} {Calendar.strftime(row.timestamp, "%Y-%m-%d %H:%M UTC")} {duplicate_source(row.reason)}

100} class="text-xs opacity-60 mt-1"> Showing first 100 of {@duplicate_count} duplicates.

0}>

Valid rows ready to import ({@valid_count})

Row Station 1 Station 2 Band Mode Timestamp
Row {row.row_num} {row.attrs["station1"]} {row.attrs["grid1"]} {row.attrs["station2"]} {row.attrs["grid2"]} {row.attrs["band"]} {row.attrs["mode"]} {Calendar.strftime(row.timestamp, "%Y-%m-%d %H:%M UTC")}

length(@valid_sample)} class="text-xs opacity-60 mt-1"> Showing first {length(@valid_sample)} of {@valid_count} valid rows.

""" end attr :tone, :string, required: true attr :label, :string, required: true attr :value, :integer, required: true attr :hint, :string, required: true @doc false @spec summary_card(map()) :: Rendered.t() def summary_card(assigns) do ~H"""
"border-success/30 bg-success/10" "warning" -> "border-warning/30 bg-warning/10" "error" -> "border-error/30 bg-error/10" "info" -> "border-info/30 bg-info/10" _ -> "border-base-300 bg-base-200" end ]}>
{@label}
{@value}
{@hint}
""" end defp duplicate_source(:existing_contact), do: "Already in database" defp duplicate_source(:earlier_in_upload), do: "Earlier row in this upload" defp duplicate_source(_), do: "Duplicate" defp pluralize(1, word), do: word defp pluralize(_, word), do: word <> "s" defp confirm_button_label(valid, 0) do "Looks good — submit #{valid} #{pluralize(valid, "contact")}" end defp confirm_button_label(0, refined) do "Looks good — refine #{refined} existing #{pluralize(refined, "contact")}" end defp confirm_button_label(valid, refined) do "Looks good — submit #{valid} and refine #{refined}" end defp confirm_prompt(valid, 0), do: "Import #{valid} contacts?" defp confirm_prompt(0, refined), do: "Refine #{refined} existing contacts?" defp confirm_prompt(valid, refined) do "Import #{valid} contacts and refine #{refined} existing?" end end