diff --git a/lib/bike_brigade/locations.ex b/lib/bike_brigade/locations.ex index e4568a08..8a42c904 100644 --- a/lib/bike_brigade/locations.ex +++ b/lib/bike_brigade/locations.ex @@ -21,7 +21,10 @@ defmodule BikeBrigade.Locations do Returns the list of community_fridges. """ def list_community_fridges do - Repo.all(from cf in CommunityFridge, order_by: [desc: cf.active, asc: cf.name]) + CommunityFridge + |> from(order_by: [desc: :active, asc: :name]) + |> preload(:location) + |> Repo.all() end @doc """ @@ -29,13 +32,18 @@ defmodule BikeBrigade.Locations do Raises `Ecto.NoResultsError` if the Community fridge does not exist. """ - def get_community_fridge!(id), do: Repo.get!(CommunityFridge, id) + def get_community_fridge!(id) do + CommunityFridge + |> where([cf], cf.id == ^id) + |> preload(:location) + |> Repo.one!() + end @doc """ Creates a community_fridge. """ def create_community_fridge(attrs \\ %{}) do - %CommunityFridge{} + %CommunityFridge{location: nil} |> CommunityFridge.changeset(attrs) |> Repo.insert() end @@ -45,6 +53,7 @@ defmodule BikeBrigade.Locations do """ def update_community_fridge(%CommunityFridge{} = community_fridge, attrs) do community_fridge + |> Repo.preload(:location) |> CommunityFridge.changeset(attrs) |> Repo.update() end diff --git a/lib/bike_brigade/locations/community_fridge.ex b/lib/bike_brigade/locations/community_fridge.ex index fb00feba..1515556d 100644 --- a/lib/bike_brigade/locations/community_fridge.ex +++ b/lib/bike_brigade/locations/community_fridge.ex @@ -19,16 +19,15 @@ defmodule BikeBrigade.Locations.CommunityFridge do field :active, :boolean, default: true field :pair_preferred, :boolean, default: false - belongs_to :location, Location + belongs_to :location, Location, on_replace: :update timestamps() end def changeset(community_fridge, params) do community_fridge - |> cast(params, @fields ++ [:location_id]) - |> validate_required([:name, :location_id]) - |> unique_constraint(:location_id) - |> assoc_constraint(:location) + |> cast(params, @fields) + |> cast_assoc(:location) + |> validate_required([:name]) end end diff --git a/lib/bike_brigade_web/components/layouts.ex b/lib/bike_brigade_web/components/layouts.ex index 7771b87d..9685aa26 100644 --- a/lib/bike_brigade_web/components/layouts.ex +++ b/lib/bike_brigade_web/components/layouts.ex @@ -32,6 +32,7 @@ defmodule BikeBrigadeWeb.Layouts do :campaigns, :opportunities, :riders, + :community_fridges, :stats, :users, :messages, @@ -69,6 +70,15 @@ defmodule BikeBrigadeWeb.Layouts do Riders + <.sidebar_link + selected={@current_page == :community_fridges} + navigate={~p"/community_fridges"} + > + <:icon> + + + Community Fridges + <.sidebar_link selected={@current_page == :stats} navigate={~p"/stats"}> <:icon> diff --git a/lib/bike_brigade_web/live/community_fridge_live/form_component.ex b/lib/bike_brigade_web/live/community_fridge_live/form_component.ex new file mode 100644 index 00000000..518e8bd3 --- /dev/null +++ b/lib/bike_brigade_web/live/community_fridge_live/form_component.ex @@ -0,0 +1,96 @@ +defmodule BikeBrigadeWeb.CommunityFridgeLive.FormComponent do + use BikeBrigadeWeb, :live_component + + alias BikeBrigade.{Locations, MediaStorage} + alias BikeBrigadeWeb.Components.LiveLocation + + @impl true + def mount(socket) do + {:ok, + allow_upload(socket, :photo, + accept: ~w(.gif .png .jpg .jpeg), + max_entries: 1, + max_file_size: 10_000_000 + )} + end + + @impl true + def update(%{community_fridge: community_fridge} = assigns, socket) do + changeset = Locations.change_community_fridge(community_fridge) + + {:ok, + socket + |> assign(assigns) + |> assign(:changeset, changeset)} + end + + @impl true + def handle_event("validate", %{"community_fridge" => params}, socket) do + changeset = + socket.assigns.community_fridge + |> Locations.change_community_fridge(params) + |> Map.put(:action, :validate) + + {:noreply, assign(socket, :changeset, changeset)} + end + + def handle_event("save", %{"community_fridge" => params}, socket) do + params = + case consume_uploaded_entries(socket, :photo, fn %{path: path}, + %{client_type: content_type} -> + {:ok, MediaStorage.upload_file!(path, content_type)} + end) do + [%{url: url}] -> Map.put(params, "photo", url) + [] -> Map.update(params, "photo", nil, &if(&1 == "", do: nil, else: &1)) + end + + save_community_fridge(socket, socket.assigns.action, params) + end + + def handle_event("delete_photo", _params, socket) do + params = socket.assigns.changeset.params || %{} + + {:noreply, + socket + |> assign( + :changeset, + socket.assigns.community_fridge + |> Locations.change_community_fridge(Map.put(params, "photo", nil)) + |> Map.put(:action, :validate) + )} + end + + def handle_event("cancel_upload", %{"ref" => ref}, socket) do + {:noreply, cancel_upload(socket, :photo, ref)} + end + + defp error_to_string(:too_large), do: "Too large" + defp error_to_string(:too_many_files), do: "You have selected too many files" + defp error_to_string(:not_accepted), do: "You have selected an unacceptable file type" + + defp save_community_fridge(socket, :edit, params) do + case Locations.update_community_fridge(socket.assigns.community_fridge, params) do + {:ok, _community_fridge} -> + {:noreply, + socket + |> put_flash(:info, "Community fridge updated successfully") + |> push_patch(to: socket.assigns.navigate)} + + {:error, %Ecto.Changeset{} = changeset} -> + {:noreply, assign(socket, :changeset, changeset)} + end + end + + defp save_community_fridge(socket, :new, params) do + case Locations.create_community_fridge(params) do + {:ok, _community_fridge} -> + {:noreply, + socket + |> put_flash(:info, "Community fridge created successfully") + |> push_patch(to: socket.assigns.navigate)} + + {:error, %Ecto.Changeset{} = changeset} -> + {:noreply, assign(socket, changeset: changeset)} + end + end +end diff --git a/lib/bike_brigade_web/live/community_fridge_live/form_component.html.heex b/lib/bike_brigade_web/live/community_fridge_live/form_component.html.heex new file mode 100644 index 00000000..801babaa --- /dev/null +++ b/lib/bike_brigade_web/live/community_fridge_live/form_component.html.heex @@ -0,0 +1,100 @@ +
+ <.header>{@title} + <.simple_form + :let={f} + for={@changeset} + id="community-fridge-form" + phx-target={@myself} + phx-change="validate" + phx-submit="save" + > + <.input type="text" field={f[:name]} label="Name" /> + + <.input type="textarea" field={f[:description]} label="Description" /> + + <.live_component + module={LiveLocation} + id="location-form" + field={f[:location]} + label="Location" + /> + +
+ + +
+ +
+ <.button + color={:lightred} + size={:xsmall} + phx-click={JS.push("delete_photo", target: @myself)} + type="button" + > + Delete + +
+
+
+
+ +
+ +

or drag and drop

+
+

PNG, JPG, GIF up to 10MB

+
+
+
+
+ <.live_img_preview entry={entry} class="w-16 rounded" /> +
{entry.client_name}
+ +

+ {error_to_string(err)} +

+
+
+
+ + <.input + type="checkbox" + field={f[:active]} + label="Active" + /> + + <.input + type="checkbox" + field={f[:pair_preferred]} + label="Pair Preferred" + help_text="Deliveries to this fridge prefer a rider pair" + /> + + <:actions> + <.button navigate={~p"/community_fridges"} color={:white}> + Cancel + + <.button type="submit" phx-disable-with="Saving...">Save + + +
diff --git a/lib/bike_brigade_web/live/community_fridge_live/index.ex b/lib/bike_brigade_web/live/community_fridge_live/index.ex new file mode 100644 index 00000000..e1d0ddb8 --- /dev/null +++ b/lib/bike_brigade_web/live/community_fridge_live/index.ex @@ -0,0 +1,93 @@ +defmodule BikeBrigadeWeb.CommunityFridgeLive.Index do + use BikeBrigadeWeb, :live_view + + alias BikeBrigade.Locations + alias BikeBrigade.Locations.CommunityFridge + + # Toronto city hall as fallback centre + @default_coords %Geo.Point{coordinates: {-79.3832, 43.6532}} + + @impl Phoenix.LiveView + def mount(_params, _session, socket) do + {:ok, + socket + |> assign(:page, :community_fridges) + |> assign(:page_title, "Community Fridges") + |> assign(:community_fridge, nil) + |> assign(:community_fridges, Locations.list_community_fridges()) + |> assign(:map_layers, []) + |> assign(:map_coords, @default_coords) + |> assign(:mode, :list)} + end + + @impl Phoenix.LiveView + def handle_params(params, _url, socket) do + {:noreply, apply_action(socket, socket.assigns.live_action, params)} + end + + @impl Phoenix.LiveView + def handle_event("set_mode", %{"mode" => mode}, socket) do + socket = assign(socket, :mode, String.to_existing_atom(mode)) + + socket = + if socket.assigns.mode == :map do + push_event(socket, "leaflet:redraw_map", %{recenter: false}) + else + socket + end + + {:noreply, socket} + end + + defp apply_action(socket, :index, _params) do + community_fridges = Locations.list_community_fridges() + + socket + |> assign(:page_title, "Community Fridges") + |> assign(:community_fridge, nil) + |> assign(:community_fridges, community_fridges) + |> assign(:map_layers, fridge_markers(community_fridges)) + |> assign(:map_coords, map_center(community_fridges)) + end + + defp apply_action(socket, :new, _params) do + socket + |> assign(:page_title, "New Fridge") + |> assign(:community_fridge, %CommunityFridge{location: nil}) + end + + defp apply_action(socket, :edit, %{"id" => id}) do + socket + |> assign(:page_title, "Edit Fridge") + |> assign(:community_fridge, Locations.get_community_fridge!(id)) + end + + defp fridge_markers(community_fridges) do + for %{id: id, name: name, location: location} <- community_fridges, + not is_nil(location), + not is_nil(location.coords) do + %{ + id: "fridge-#{id}", + type: :marker, + data: %{ + lat: lat(location), + lng: lng(location), + icon: "warehouse", + color: "#1c64f2", + tooltip: name + } + } + end + end + + defp map_center(community_fridges) do + community_fridges + |> Enum.find_value(fn %{location: location} -> + location && location.coords + end) + |> case do + nil -> @default_coords + coords -> coords + end + end +end diff --git a/lib/bike_brigade_web/live/community_fridge_live/index.html.heex b/lib/bike_brigade_web/live/community_fridge_live/index.html.heex new file mode 100644 index 00000000..92cd6840 --- /dev/null +++ b/lib/bike_brigade_web/live/community_fridge_live/index.html.heex @@ -0,0 +1,80 @@ +<.header> + Community Fridges + <:subtitle> + Community fridges are neighbourhood food-sharing locations. Dispatchers can manage fridge details here. + + +
+
+ + +
+ <.button patch={~p"/community_fridges/new"}> + New Community Fridge + +
+ +<%= if @mode == :map do %> +
+
+ <.map + id="community-fridges-map" + coords={@map_coords} + initial_layers={@map_layers} + class="h-full" + /> +
+
+<% else %> + <.table id="community_fridges" rows={@community_fridges}> + <:col :let={community_fridge} label="Name"> + {community_fridge.name} + + <:col :let={community_fridge} label="Address"> + {community_fridge.location} + + <:col :let={community_fridge} label="Description"> + {community_fridge.description} + + <:col :let={community_fridge} label="Pair Preferred"> + {if community_fridge.pair_preferred, do: "Yes", else: "No"} + + <:col :let={community_fridge} label="Active"> + {if community_fridge.active, do: "Yes", else: "No"} + + + <:action :let={community_fridge}> + <.link patch={~p"/community_fridges/#{community_fridge.id}/edit"} class="link"> + Edit + + + +<% end %> + +<.modal + :if={@live_action in [:new, :edit]} + id="community-fridge-modal" + show + on_cancel={JS.navigate(~p"/community_fridges")} +> + <.live_component + module={BikeBrigadeWeb.CommunityFridgeLive.FormComponent} + id={@community_fridge.id || :new} + action={@live_action} + title={@page_title} + community_fridge={@community_fridge} + navigate={~p"/community_fridges"} + /> + diff --git a/lib/bike_brigade_web/router.ex b/lib/bike_brigade_web/router.ex index aa288e6a..3080f591 100644 --- a/lib/bike_brigade_web/router.ex +++ b/lib/bike_brigade_web/router.ex @@ -180,6 +180,10 @@ defmodule BikeBrigadeWeb.Router do live "/tags/:id/edit", TagLive.Index, :edit live "/delivery_notes", DeliveryNoteLive.Index, :index + + live "/community_fridges", CommunityFridgeLive.Index, :index + live "/community_fridges/new", CommunityFridgeLive.Index, :new + live "/community_fridges/:id/edit", CommunityFridgeLive.Index, :edit end get "/stats/leaderboard/download", ExportStatsController, :leaderboard diff --git a/priv/repo/migrations/20261016222054_remove_location_id_from_community_fridges.exs b/priv/repo/migrations/20261016222054_remove_location_id_from_community_fridges.exs new file mode 100644 index 00000000..9572ee60 --- /dev/null +++ b/priv/repo/migrations/20261016222054_remove_location_id_from_community_fridges.exs @@ -0,0 +1,7 @@ +defmodule BikeBrigade.Repo.Migrations.RemoveLocationIdFromCommunityFridges do + use Ecto.Migration + + def change do + drop unique_index(:community_fridges, [:location_id]) + end +end diff --git a/priv/repo/seeds/toronto.ex b/priv/repo/seeds/toronto.ex index bd5252bb..a1e45341 100644 --- a/priv/repo/seeds/toronto.ex +++ b/priv/repo/seeds/toronto.ex @@ -30,7 +30,7 @@ defmodule BikeBrigade.Repo.Seeds.Toronto do city: city, country: "Canada", province: "Ontario", - coords: %Geo.Point{coordinates: {lon, lat}} + coords: %Geo.Point{coordinates: {lon, lat}, srid: 4326} } end end diff --git a/test/bike_brigade/locations/community_fridge_test.exs b/test/bike_brigade/locations/community_fridge_test.exs index c8b3dc46..e2e4004c 100644 --- a/test/bike_brigade/locations/community_fridge_test.exs +++ b/test/bike_brigade/locations/community_fridge_test.exs @@ -1,69 +1,62 @@ defmodule BikeBrigade.Locations.CommunityFridgeTest do use BikeBrigade.DataCase - alias BikeBrigade.Locations.Location + alias BikeBrigade.Locations alias BikeBrigade.Locations.CommunityFridge - - setup do - location_data = fixture(:location) - - {:ok, location} = - %Location{} - |> Location.changeset(location_data) - |> Repo.insert() - - %{location: location} - end + alias BikeBrigade.Repo.Seeds.Toronto describe "CommunityFridge changeset" do - test "changeset with valid attributes", %{location: location} do + test "valid with nested location params" do attrs = %{ name: "Downtown Community Fridge", description: "A fridge for the community", - photo: "https://example.com/photo.jpg", active: true, pair_preferred: false, - location_id: location.id + location: Toronto.random_location() } - changeset = CommunityFridge.changeset(%CommunityFridge{}, attrs) + changeset = CommunityFridge.changeset(%CommunityFridge{location: nil}, attrs) assert changeset.valid? end - test "changeset with minimal required attributes", %{location: location} do - attrs = %{ - name: "Community Fridge", - location_id: location.id - } - - changeset = CommunityFridge.changeset(%CommunityFridge{}, attrs) + test "valid without a location" do + changeset = + CommunityFridge.changeset(%CommunityFridge{location: nil}, %{name: "Bare Fridge"}) assert changeset.valid? end - test "changeset requires name", %{location: location} do - attrs = %{ - description: "A fridge without a name", - location_id: location.id - } - - changeset = CommunityFridge.changeset(%CommunityFridge{}, attrs) + test "requires name" do + changeset = + CommunityFridge.changeset(%CommunityFridge{location: nil}, %{ + location: Toronto.random_location() + }) refute changeset.valid? assert "can't be blank" in errors_on(changeset).name end - test "changeset rejects empty name", %{location: location} do - attrs = %{ - name: "", - location_id: location.id - } - - changeset = CommunityFridge.changeset(%CommunityFridge{}, attrs) + test "rejects empty name" do + changeset = + CommunityFridge.changeset(%CommunityFridge{location: nil}, %{ + name: "", + location: Toronto.random_location() + }) refute changeset.valid? assert "can't be blank" in errors_on(changeset).name end + + test "updating location updates the associated record in place" do + fridge = fixture(:community_fridge) + original_location_id = fridge.location.id + + new_location = Toronto.random_location() + {:ok, updated} = Locations.update_community_fridge(fridge, %{location: new_location}) + + assert updated.location.id == original_location_id + assert updated.location.address == new_location.address + end end end diff --git a/test/bike_brigade/locations_test.exs b/test/bike_brigade/locations_test.exs index 3e647d18..bf7aa264 100644 --- a/test/bike_brigade/locations_test.exs +++ b/test/bike_brigade/locations_test.exs @@ -26,23 +26,30 @@ defmodule BikeBrigade.LocationsTest do test "create_community_fridge/1 with valid data creates a community_fridge" do location = fixture(:persisted_location, %{}) - valid_attrs = %{ - active: true, - name: "some name", - description: "some description", - photo: "some photo", - pair_preferred: true, - location_id: location.id - } - - assert {:ok, %CommunityFridge{} = community_fridge} = - Locations.create_community_fridge(valid_attrs) - - assert community_fridge.active == true - assert community_fridge.name == "some name" - assert community_fridge.description == "some description" - assert community_fridge.photo == "some photo" - assert community_fridge.pair_preferred == true + %{ + active: active, + name: name, + description: description, + photo: photo, + pair_preferred: pair_preferred + } = + valid_attrs = %{ + active: true, + name: "some name", + description: "some description", + photo: "some photo", + pair_preferred: true, + location_id: location.id + } + + assert {:ok, + %CommunityFridge{ + active: ^active, + name: ^name, + description: ^description, + photo: ^photo, + pair_preferred: ^pair_preferred + }} = Locations.create_community_fridge(valid_attrs) end test "create_community_fridge/1 with invalid data returns error changeset" do @@ -52,19 +59,20 @@ defmodule BikeBrigade.LocationsTest do test "update_community_fridge/2 with valid data updates the community_fridge", %{ community_fridge: community_fridge } do - active = false - name = "some updated name" - description = "some updated description" - photo = "some updated photo" - pair_preferred = false - - update_attrs = %{ + %{ active: active, name: name, description: description, photo: photo, pair_preferred: pair_preferred - } + } = + update_attrs = %{ + active: false, + name: "some updated name", + description: "some updated description", + photo: "some updated photo", + pair_preferred: false + } assert {:ok, %CommunityFridge{ @@ -76,15 +84,6 @@ defmodule BikeBrigade.LocationsTest do }} = Locations.update_community_fridge(community_fridge, update_attrs) end - test "update_community_fridge/2 with invalid data returns error changeset", %{ - community_fridge: community_fridge - } do - assert {:error, %Ecto.Changeset{}} = - Locations.update_community_fridge(community_fridge, @invalid_attrs) - - assert community_fridge == Locations.get_community_fridge!(community_fridge.id) - end - test "delete_community_fridge/1 deletes the community_fridge", %{ community_fridge: community_fridge } do diff --git a/test/bike_brigade_web/live/campaign_live_test.exs b/test/bike_brigade_web/live/campaign_live_test.exs index 48b9c557..023bb349 100644 --- a/test/bike_brigade_web/live/campaign_live_test.exs +++ b/test/bike_brigade_web/live/campaign_live_test.exs @@ -78,7 +78,7 @@ defmodule BikeBrigadeWeb.CampaignLiveTest do coords: %Geo.Point{ coordinates: {-79.37761739999999, 43.6459904}, - srid: nil, + srid: 4326, properties: %{} } |> Geo.JSON.encode!() diff --git a/test/bike_brigade_web/live/community_fridge_live_test.exs b/test/bike_brigade_web/live/community_fridge_live_test.exs new file mode 100644 index 00000000..9710d501 --- /dev/null +++ b/test/bike_brigade_web/live/community_fridge_live_test.exs @@ -0,0 +1,174 @@ +defmodule BikeBrigadeWeb.CommunityFridgeLiveTest do + use BikeBrigadeWeb.ConnCase + import Phoenix.LiveViewTest + + describe "Community Fridges index (dispatcher)" do + setup [:login] + + test "lists all community fridges", ctx do + fridge = fixture(:community_fridge, %{name: "Test Fridge"}) + {:ok, _view, html} = live(ctx.conn, ~p"/community_fridges") + + assert html =~ "Community Fridges" + assert html =~ fridge.name + end + + test "shows address column for fridge with a location", ctx do + fridge = fixture(:community_fridge, %{name: "Located Fridge"}) + {:ok, _view, html} = live(ctx.conn, ~p"/community_fridges") + + assert html =~ fridge.location.address + end + + test "renders map tab and displays map on click", ctx do + {:ok, view, html} = live(ctx.conn, ~p"/community_fridges") + + refute html =~ "community-fridges-map" + assert html =~ "Map" + + html = view |> element("button", "Map") |> render_click() + + assert html =~ "community-fridges-map" + end + + test "can create a new community fridge", ctx do + location = fixture(:persisted_location, %{}) + + {:ok, view, _html} = live(ctx.conn, ~p"/community_fridges") + + view |> element("a", "New Community Fridge") |> render_click() + assert_patched(view, ~p"/community_fridges/new") + + view + |> form("#community-fridge-form", community_fridge: %{name: "Brand New Fridge"}) + |> render_submit(%{ + community_fridge: %{ + location: %{ + id: location.id, + address: location.address, + coords: location.coords |> Geo.JSON.encode!() |> Jason.encode!() + } + } + }) + + assert_patch(view, ~p"/community_fridges") + html = render(view) + assert html =~ "Community fridge created successfully" + assert html =~ "Brand New Fridge" + end + + test "validates name is required", ctx do + {:ok, view, _html} = live(ctx.conn, ~p"/community_fridges/new") + + html = + view + |> form("#community-fridge-form", community_fridge: %{name: ""}) + |> render_submit() + + assert html =~ "can't be blank" + end + + test "can edit a community fridge", ctx do + fridge = fixture(:community_fridge, %{name: "Original Fridge"}) + {:ok, view, _html} = live(ctx.conn, ~p"/community_fridges") + + view |> element("a", "Edit") |> render_click() + assert_patched(view, ~p"/community_fridges/#{fridge.id}/edit") + + view + |> form("#community-fridge-form", community_fridge: %{name: "Updated Fridge"}) + |> render_submit() + + assert_patch(view, ~p"/community_fridges") + html = render(view) + assert html =~ "Community fridge updated successfully" + assert html =~ "Updated Fridge" + refute html =~ "Original Fridge" + end + + test "can navigate directly to edit page", ctx do + fridge = fixture(:community_fridge, %{name: "Edit Me"}) + {:ok, _view, html} = live(ctx.conn, ~p"/community_fridges/#{fridge.id}/edit") + + assert html =~ "Edit Fridge" + end + + test "edit preserves existing values", ctx do + fridge = + fixture(:community_fridge, %{ + name: "Preserve Me", + description: "Some description", + active: true, + pair_preferred: true + }) + + {:ok, _view, html} = live(ctx.conn, ~p"/community_fridges/#{fridge.id}/edit") + + assert html =~ "Preserve Me" + assert html =~ "Some description" + end + end + + describe "Community Fridge photo upload" do + setup [:login] + + test "shows existing photo in edit form", ctx do + fridge = + fixture(:community_fridge, %{ + name: "Photo Fridge", + photo: "https://example.com/fridge.jpg" + }) + + {:ok, _view, html} = live(ctx.conn, ~p"/community_fridges/#{fridge.id}/edit") + + assert html =~ "https://example.com/fridge.jpg" + end + + test "renders upload dropzone", ctx do + fixture(:community_fridge, %{name: "Upload Fridge"}) + {:ok, view, _html} = live(ctx.conn, ~p"/community_fridges/new") + + html = render(view) + assert html =~ "Upload a file" + assert html =~ "or drag and drop" + assert html =~ "PNG, JPG, GIF" + end + + test "shows pending upload preview after file is selected", ctx do + {:ok, view, _html} = live(ctx.conn, ~p"/community_fridges/new") + + upload = + file_input(view, "#community-fridge-form", :photo, [ + %{name: "fridge.jpg", content: <<0, 1, 2>>, type: "image/jpeg"} + ]) + + render_upload(upload, "fridge.jpg", 100) + + html = render(view) + assert html =~ "fridge.jpg" + end + + test "can cancel a pending upload", ctx do + {:ok, view, _html} = live(ctx.conn, ~p"/community_fridges/new") + + upload = + file_input(view, "#community-fridge-form", :photo, [ + %{name: "fridge.jpg", content: <<0, 1, 2>>, type: "image/jpeg"} + ]) + + render_upload(upload, "fridge.jpg", 100) + assert render(view) =~ "fridge.jpg" + + view |> element("button[aria-label='cancel']") |> render_click() + refute render(view) =~ "fridge.jpg" + end + end + + describe "Community Fridges access control" do + setup [:login_as_rider] + + test "non-dispatchers cannot access community fridges page", ctx do + assert {:error, {:redirect, %{to: "/login"}}} = live(ctx.conn, ~p"/community_fridges") + end + end +end diff --git a/test/support/fixtures.ex b/test/support/fixtures.ex index e392f86e..d96147d3 100644 --- a/test/support/fixtures.ex +++ b/test/support/fixtures.ex @@ -193,15 +193,10 @@ defmodule BikeBrigade.Fixtures do end def fixture(:community_fridge, attrs) do - attrs = - attrs - |> Map.put_new_lazy(:location_id, fn -> fixture(:persisted_location, %{}).id end) + attrs = Map.put_new_lazy(attrs, :location, fn -> Toronto.random_location() end) {:ok, community_fridge} = - %{ - name: Faker.Company.name(), - active: true - } + %{name: Faker.Company.name(), active: true} |> Map.merge(attrs) |> Locations.create_community_fridge()