Link submissions to the logged-in user on /submit
Contacts now carry a user_id FK to users. When a logged-in user submits a contact (single or CSV upload), the server injects their user_id and the email from their account into the submission params, and the visible email input is replaced with a hidden input holding that value — logged-in users don't have to retype their address. The submission changeset now requires at least one of user_id or submitter_email (rather than hard-requiring email), and email format is only validated when one is actually present. Reworded the submit-page blurb from "10 GHz to 241 GHz" to "902 MHz and up" so it matches the actual band coverage of the project.
This commit is contained in:
parent
8637253fda
commit
5724b33e8f
3 changed files with 106 additions and 30 deletions
|
|
@ -39,13 +39,14 @@ defmodule Microwaveprop.Radio.Contact do
|
|||
|
||||
field :user_submitted, :boolean, default: false
|
||||
field :submitter_email, :string
|
||||
field :user_id, :binary_id
|
||||
field :flagged_invalid, :boolean, default: false
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
|
||||
@required_fields ~w(station1 station2 qso_timestamp mode band)a
|
||||
@optional_fields ~w(grid1 grid2 pos1 pos2 distance_km)a
|
||||
@optional_fields ~w(grid1 grid2 pos1 pos2 distance_km user_id)a
|
||||
|
||||
def changeset(contact, attrs) do
|
||||
contact
|
||||
|
|
@ -53,14 +54,16 @@ defmodule Microwaveprop.Radio.Contact do
|
|||
|> validate_required(@required_fields)
|
||||
end
|
||||
|
||||
@submission_fields ~w(station1 station2 qso_timestamp mode band grid1 grid2 submitter_email)a
|
||||
@submission_fields ~w(station1 station2 qso_timestamp mode band grid1 grid2 submitter_email user_id)a
|
||||
@submission_required ~w(station1 station2 qso_timestamp mode band grid1 grid2)a
|
||||
@allowed_modes ~w(CW SSB FM FT8 FT4)
|
||||
@allowed_bands Enum.map(~w(1296 2304 3456 5760 10000 24000 47000 68000 75000 122000 134000 241000), &Decimal.new/1)
|
||||
|
||||
def submission_changeset(contact, attrs) do
|
||||
contact
|
||||
|> cast(attrs, @submission_fields)
|
||||
|> validate_required(@submission_fields)
|
||||
|> validate_required(@submission_required)
|
||||
|> validate_user_or_email()
|
||||
|> sanitize_callsign(:station1)
|
||||
|> sanitize_callsign(:station2)
|
||||
|> sanitize_grid(:grid1)
|
||||
|
|
@ -71,12 +74,33 @@ defmodule Microwaveprop.Radio.Contact do
|
|||
|> validate_grid_format(:grid2)
|
||||
|> validate_inclusion(:mode, @allowed_modes)
|
||||
|> validate_inclusion(:band, @allowed_bands)
|
||||
|> validate_format(:submitter_email, ~r/^[^\s<>]+@[^\s<>]+\.[^\s<>]+$/)
|
||||
|> validate_email_format()
|
||||
|> validate_length(:submitter_email, max: 254)
|
||||
|> validate_length(:station1, max: 20)
|
||||
|> validate_length(:station2, max: 20)
|
||||
end
|
||||
|
||||
# Either a user_id (logged-in submitter) or a submitter_email (anonymous)
|
||||
# must be present so we know who submitted the contact.
|
||||
defp validate_user_or_email(changeset) do
|
||||
user_id = get_field(changeset, :user_id)
|
||||
email = get_field(changeset, :submitter_email)
|
||||
|
||||
if user_id || (email && email != "") do
|
||||
changeset
|
||||
else
|
||||
add_error(changeset, :submitter_email, "can't be blank")
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_email_format(changeset) do
|
||||
case get_field(changeset, :submitter_email) do
|
||||
nil -> changeset
|
||||
"" -> changeset
|
||||
_email -> validate_format(changeset, :submitter_email, ~r/^[^\s<>]+@[^\s<>]+\.[^\s<>]+$/)
|
||||
end
|
||||
end
|
||||
|
||||
# Strip whitespace and upcase callsigns
|
||||
defp sanitize_callsign(changeset, field) do
|
||||
case get_change(changeset, field) do
|
||||
|
|
|
|||
|
|
@ -66,6 +66,8 @@ defmodule MicrowavepropWeb.SubmitLive do
|
|||
if recently_submitted?(socket) do
|
||||
{:noreply, put_flash(socket, :error, "Please wait before submitting another contact.")}
|
||||
else
|
||||
contact_params = merge_user_params(contact_params, socket)
|
||||
|
||||
case Radio.create_contact(contact_params) do
|
||||
{:ok, contact} ->
|
||||
ContactWeatherEnqueueWorker.enqueue_for_contact(contact)
|
||||
|
|
@ -82,8 +84,14 @@ defmodule MicrowavepropWeb.SubmitLive do
|
|||
end
|
||||
end
|
||||
|
||||
def handle_event("upload_csv", %{"submitter_email" => email}, socket) do
|
||||
if String.trim(email) == "" do
|
||||
def handle_event("upload_csv", params, socket) do
|
||||
email =
|
||||
case current_user(socket.assigns) do
|
||||
%{email: user_email} -> user_email
|
||||
_ -> params |> Map.get("submitter_email", "") |> String.trim()
|
||||
end
|
||||
|
||||
if email == "" do
|
||||
{:noreply, put_flash(socket, :error, "Email is required for CSV upload")}
|
||||
else
|
||||
uploaded_contents =
|
||||
|
|
@ -144,6 +152,18 @@ defmodule MicrowavepropWeb.SubmitLive do
|
|||
end
|
||||
end
|
||||
|
||||
defp merge_user_params(params, socket) do
|
||||
case current_user(socket.assigns) do
|
||||
%{id: user_id, email: user_email} ->
|
||||
params
|
||||
|> Map.put("user_id", user_id)
|
||||
|> Map.put("submitter_email", user_email)
|
||||
|
||||
_ ->
|
||||
params
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
|
|
@ -156,10 +176,10 @@ defmodule MicrowavepropWeb.SubmitLive do
|
|||
<div class="bg-info/10 border border-info/30 rounded-box p-4 mb-6 text-sm leading-relaxed">
|
||||
<p class="font-semibold text-info mb-1">Every contact matters</p>
|
||||
<p>
|
||||
Our propagation model improves with real-world data. Each verified microwave contact
|
||||
Our propagation model improves with real-world data. Each verified contact
|
||||
you submit is matched against atmospheric conditions at the time of your contact,
|
||||
helping us calibrate predictions for all bands from 10 GHz to 241 GHz. The more contacts
|
||||
we have, the better our forecasts get for everyone.
|
||||
helping us calibrate predictions for all amateur bands from 902 MHz and up. The
|
||||
more contacts we have, the better our forecasts get for everyone.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -199,7 +219,12 @@ defmodule MicrowavepropWeb.SubmitLive do
|
|||
</div>
|
||||
|
||||
<%= if @active_tab == :single do %>
|
||||
<.single_contact_form form={@form} band_options={@band_options} mode_options={@mode_options} />
|
||||
<.single_contact_form
|
||||
form={@form}
|
||||
band_options={@band_options}
|
||||
mode_options={@mode_options}
|
||||
current_user={current_user(assigns)}
|
||||
/>
|
||||
<% else %>
|
||||
<%= cond do %>
|
||||
<% @csv_result -> %>
|
||||
|
|
@ -207,13 +232,17 @@ defmodule MicrowavepropWeb.SubmitLive do
|
|||
<% @csv_preview -> %>
|
||||
<.csv_preview preview={@csv_preview} />
|
||||
<% true -> %>
|
||||
<.csv_upload_form uploads={@uploads} />
|
||||
<.csv_upload_form uploads={@uploads} current_user={current_user(assigns)} />
|
||||
<% end %>
|
||||
<% end %>
|
||||
</Layouts.app>
|
||||
"""
|
||||
end
|
||||
|
||||
# In-template variant — takes an assigns map directly (not a socket).
|
||||
defp current_user(%{current_scope: %{user: %{} = user}}), do: user
|
||||
defp current_user(_), do: nil
|
||||
|
||||
defp single_contact_form(assigns) do
|
||||
~H"""
|
||||
<.form for={@form} id="contact-form" phx-change="validate" phx-submit="save" class="space-y-4">
|
||||
|
|
@ -262,13 +291,21 @@ defmodule MicrowavepropWeb.SubmitLive do
|
|||
<.input field={@form[:qso_timestamp]} type="datetime-local" label="Timestamp (UTC)" required />
|
||||
</div>
|
||||
|
||||
<.input
|
||||
field={@form[:submitter_email]}
|
||||
type="email"
|
||||
label="Your Email"
|
||||
placeholder="you@example.com"
|
||||
required
|
||||
/>
|
||||
<%= if @current_user do %>
|
||||
<input
|
||||
type="hidden"
|
||||
name="contact[submitter_email]"
|
||||
value={@current_user.email}
|
||||
/>
|
||||
<% else %>
|
||||
<.input
|
||||
field={@form[:submitter_email]}
|
||||
type="email"
|
||||
label="Your Email"
|
||||
placeholder="you@example.com"
|
||||
required
|
||||
/>
|
||||
<% end %>
|
||||
|
||||
<div class="mt-6">
|
||||
<.button phx-disable-with="Submitting..." class="btn btn-primary btn-lg w-full sm:w-auto">
|
||||
|
|
@ -311,18 +348,22 @@ defmodule MicrowavepropWeb.SubmitLive do
|
|||
</label>
|
||||
</div>
|
||||
|
||||
<div class="fieldset mb-2">
|
||||
<label>
|
||||
<span class="label mb-1">Your Email</span>
|
||||
<input
|
||||
type="email"
|
||||
name="submitter_email"
|
||||
class="w-full input"
|
||||
placeholder="you@example.com"
|
||||
value=""
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<%= if @current_user do %>
|
||||
<input type="hidden" name="submitter_email" value={@current_user.email} />
|
||||
<% else %>
|
||||
<div class="fieldset mb-2">
|
||||
<label>
|
||||
<span class="label mb-1">Your Email</span>
|
||||
<input
|
||||
type="email"
|
||||
name="submitter_email"
|
||||
class="w-full input"
|
||||
placeholder="you@example.com"
|
||||
value=""
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="mt-6">
|
||||
<button type="submit" class="btn btn-primary btn-lg w-full sm:w-auto">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
defmodule Microwaveprop.Repo.Migrations.AddUserIdToContacts do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:contacts) do
|
||||
add :user_id, references(:users, type: :binary_id, on_delete: :nilify_all)
|
||||
end
|
||||
|
||||
create index(:contacts, [:user_id])
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue