prop/lib/microwaveprop/rover/location.ex
Graham McIntire 140351c0f6
feat(rover-locations): add /rover-locations page with CRUD
New globally-shared list of rover-friendly (or off-limits) parking
spots. Anyone can view; logged-in users can add new locations and
edit/delete their own. Each location stores lat/lon, free-text notes,
and a status enum (:ideal | :off_limits).

  * Microwaveprop.Rover.Location schema + migration
  * Rover.list_locations/0, create_location/2, update_location/3,
    delete_location/2 (mutations require User; ownership-scoped)
  * /rover-locations LiveView in the public live_session — Add button
    is disabled for anonymous visitors, Edit/Delete buttons render
    only on the user's own entries
  * Tests for context + LiveView covering anonymous read, owner-only
    edit/delete, and form submission
2026-04-26 13:08:27 -05:00

44 lines
1.3 KiB
Elixir

defmodule Microwaveprop.Rover.Location do
@moduledoc """
A globally-visible rover parking location contributed by a logged-in
user. Everyone can view the list; only authenticated users can create
entries (creator tracked via `user_id`).
`status` is `:ideal` (recommended spot) or `:off_limits` (avoid —
trespass, no-go, etc.).
"""
use Ecto.Schema
import Ecto.Changeset
@statuses [:ideal, :off_limits]
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
schema "rover_locations" do
field :lat, :float
field :lon, :float
field :status, Ecto.Enum, values: @statuses, default: :ideal
field :notes, :string
belongs_to :user, Microwaveprop.Accounts.User
timestamps(type: :utc_datetime)
end
@type t :: %__MODULE__{}
@spec statuses() :: [atom()]
def statuses, do: @statuses
@spec changeset(t() | Ecto.Changeset.t(), map()) :: Ecto.Changeset.t()
def changeset(location, attrs) do
location
|> cast(attrs, [:lat, :lon, :status, :notes])
|> validate_required([:lat, :lon, :status])
|> validate_number(:lat, greater_than_or_equal_to: -90.0, less_than_or_equal_to: 90.0)
|> validate_number(:lon, greater_than_or_equal_to: -180.0, less_than_or_equal_to: 180.0)
|> validate_inclusion(:status, @statuses)
|> validate_length(:notes, max: 4000)
end
end