New list: orders

This commit is contained in:
2023-03-13 19:12:36 +02:00
parent 30fecdb876
commit f24dce5e51
15 changed files with 805 additions and 0 deletions

104
lib/osuuspuutarha/orders.ex Normal file
View File

@@ -0,0 +1,104 @@
defmodule Osuuspuutarha.Orders do
@moduledoc """
The Orders context.
"""
import Ecto.Query, warn: false
alias Osuuspuutarha.Repo
alias Osuuspuutarha.Orders.Order
@doc """
Returns the list of orders.
## Examples
iex> list_orders()
[%Order{}, ...]
"""
def list_orders do
Repo.all(Order)
end
@doc """
Gets a single order.
Raises `Ecto.NoResultsError` if the Order does not exist.
## Examples
iex> get_order!(123)
%Order{}
iex> get_order!(456)
** (Ecto.NoResultsError)
"""
def get_order!(id), do: Repo.get!(Order, id)
@doc """
Creates a order.
## Examples
iex> create_order(%{field: value})
{:ok, %Order{}}
iex> create_order(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_order(attrs \\ %{}) do
%Order{}
|> Order.changeset(attrs)
|> Repo.insert()
end
@doc """
Updates a order.
## Examples
iex> update_order(order, %{field: new_value})
{:ok, %Order{}}
iex> update_order(order, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def update_order(%Order{} = order, attrs) do
order
|> Order.changeset(attrs)
|> Repo.update()
end
@doc """
Deletes a order.
## Examples
iex> delete_order(order)
{:ok, %Order{}}
iex> delete_order(order)
{:error, %Ecto.Changeset{}}
"""
def delete_order(%Order{} = order) do
Repo.delete(order)
end
@doc """
Returns an `%Ecto.Changeset{}` for tracking order changes.
## Examples
iex> change_order(order)
%Ecto.Changeset{data: %Order{}}
"""
def change_order(%Order{} = order, attrs \\ %{}) do
Order.changeset(order, attrs)
end
end

View File

@@ -0,0 +1,65 @@
defmodule Osuuspuutarha.Orders.Order do
use Ecto.Schema
import Ecto.Changeset
schema "orders" do
field :address, :string
field :city, :string
field :email, :string
field :even_weeks, :boolean, default: false
field :fname, :string
field :is_member, :boolean, default: false
field :lname, :string
field :location, Ecto.Enum,
values: [
:koroinen,
:ocean,
:raisio,
:naantali,
:viherlassila,
:kirjakahvila,
:askainen,
:livonsaari
]
field :order_type, Ecto.Enum, values: [:full, :everyother, :elo, :community]
field :pcode, :string
field :phone, :string
field :split_invoice, :boolean, default: false
timestamps()
end
@doc false
def changeset(order, attrs) do
order
|> cast(attrs, [
:order_type,
:location,
:fname,
:lname,
:address,
:pcode,
:city,
:phone,
:email,
:is_member,
:split_invoice,
:even_weeks
])
|> validate_required([
:order_type,
:location,
:fname,
:lname,
:address,
:pcode,
:city,
:phone,
:email,
:is_member,
:split_invoice
])
end
end

View File

@@ -91,6 +91,7 @@ defmodule OsuuspuutarhaWeb do
# Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc) # Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc)
import Phoenix.LiveView.Helpers import Phoenix.LiveView.Helpers
import OsuuspuutarhaWeb.LiveHelpers
# Import basic rendering functionality (render, render_layout, etc) # Import basic rendering functionality (render, render_layout, etc)
import Phoenix.View import Phoenix.View

View File

@@ -0,0 +1,60 @@
defmodule OsuuspuutarhaWeb.LiveHelpers do
import Phoenix.LiveView
import Phoenix.LiveView.Helpers
alias Phoenix.LiveView.JS
@doc """
Renders a live component inside a modal.
The rendered modal receives a `:return_to` option to properly update
the URL when the modal is closed.
## Examples
<.modal return_to={Routes.order_index_path(@socket, :index)}>
<.live_component
module={OsuuspuutarhaWeb.OrderLive.FormComponent}
id={@order.id || :new}
title={@page_title}
action={@live_action}
return_to={Routes.order_index_path(@socket, :index)}
order: @order
/>
</.modal>
"""
def modal(assigns) do
assigns = assign_new(assigns, :return_to, fn -> nil end)
~H"""
<div id="modal" class="phx-modal fade-in" phx-remove={hide_modal()}>
<div
id="modal-content"
class="phx-modal-content fade-in-scale"
phx-click-away={JS.dispatch("click", to: "#close")}
phx-window-keydown={JS.dispatch("click", to: "#close")}
phx-key="escape"
>
<%= if @return_to do %>
<%= live_patch "✖",
to: @return_to,
id: "close",
class: "phx-modal-close",
phx_click: hide_modal()
%>
<% else %>
<a id="close" href="#" class="phx-modal-close" phx-click={hide_modal()}>✖</a>
<% end %>
<%= render_slot(@inner_block) %>
</div>
</div>
"""
end
defp hide_modal(js \\ %JS{}) do
js
|> JS.hide(to: "#modal", transition: "fade-out")
|> JS.hide(to: "#modal-content", transition: "fade-out-scale")
end
end

View File

@@ -0,0 +1,55 @@
defmodule OsuuspuutarhaWeb.OrderLive.FormComponent do
use OsuuspuutarhaWeb, :live_component
alias Osuuspuutarha.Orders
@impl true
def update(%{order: order} = assigns, socket) do
changeset = Orders.change_order(order)
{:ok,
socket
|> assign(assigns)
|> assign(:changeset, changeset)}
end
@impl true
def handle_event("validate", %{"order" => order_params}, socket) do
changeset =
socket.assigns.order
|> Orders.change_order(order_params)
|> Map.put(:action, :validate)
{:noreply, assign(socket, :changeset, changeset)}
end
def handle_event("save", %{"order" => order_params}, socket) do
save_order(socket, socket.assigns.action, order_params)
end
defp save_order(socket, :edit, order_params) do
case Orders.update_order(socket.assigns.order, order_params) do
{:ok, _order} ->
{:noreply,
socket
|> put_flash(:info, "Muutokset tallennettu")
|> push_redirect(to: socket.assigns.return_to)}
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, :changeset, changeset)}
end
end
defp save_order(socket, :new, order_params) do
case Orders.create_order(order_params) do
{:ok, _order} ->
{:noreply,
socket
|> put_flash(:info, "Tilauksesi on tallennettu")
|> push_redirect(to: socket.assigns.return_to)}
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, changeset: changeset)}
end
end
end

View File

@@ -0,0 +1,60 @@
<div>
<h2><%= @title %></h2>
<.form
let={f}
for={@changeset}
id="order-form"
phx-target={@myself}
phx-change="validate"
phx-submit="save">
<%= label f, :order_type, "Valitse tilausmuoto" %>
<%= select f, :order_type, Ecto.Enum.values(Osuuspuutarha.Orders.Order, :order_type), prompt: "Ei valintaa" %>
<%= error_tag f, :order_type %>
<%= label f, :location, "Valitse jakopaikka" %>
<%= select f, :location, Ecto.Enum.values(Osuuspuutarha.Orders.Order, :location), prompt: "Ei valintaa" %>
<%= error_tag f, :location %>
<%= label f, :fname, "Etunimi" %>
<%= text_input f, :fname %>
<%= error_tag f, :fname %>
<%= label f, :lname, "Sukunimi" %>
<%= text_input f, :lname %>
<%= error_tag f, :lname %>
<%= label f, :address, "Katuosoite" %>
<%= text_input f, :address %>
<%= error_tag f, :address %>
<%= label f, :pcode, "Postinumero" %>
<%= text_input f, :pcode %>
<%= error_tag f, :pcode %>
<%= label f, :city, "Postitoimipaikka" %>
<%= text_input f, :city %>
<%= error_tag f, :city %>
<%= label f, :phone, "Puhelinnumero" %>
<%= text_input f, :phone %>
<%= error_tag f, :phone %>
<%= label f, :email, "Sähköpostiosoite" %>
<%= text_input f, :email %>
<%= error_tag f, :email %>
<%= label f, :is_member, "Olen jo Livonsaaren Osuuspuutarhan jäsen" %>
<%= checkbox f, :is_member %>
<%= error_tag f, :is_member %>
<%= label f, :split_invoice, "Haluan maksaa laskun kahdessa erässä" %>
<%= checkbox f, :split_invoice %>
<%= error_tag f, :split_invoice %>
<div>
<%= submit "Tallenna", phx_disable_with: "Tallennetaan..." %>
</div>
</.form>
</div>

View File

@@ -0,0 +1,46 @@
defmodule OsuuspuutarhaWeb.OrderLive.Index do
use OsuuspuutarhaWeb, :live_view
alias Osuuspuutarha.Orders
alias Osuuspuutarha.Orders.Order
@impl true
def mount(_params, _session, socket) do
{:ok, assign(socket, :orders, list_orders())}
end
@impl true
def handle_params(params, _url, socket) do
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
end
defp apply_action(socket, :edit, %{"id" => id}) do
socket
|> assign(:page_title, "Muokkaa tilausta")
|> assign(:order, Orders.get_order!(id))
end
defp apply_action(socket, :new, _params) do
socket
|> assign(:page_title, "Satolaatikko tilaus")
|> assign(:order, %Order{})
end
defp apply_action(socket, :index, _params) do
socket
|> assign(:page_title, "Tilaukset")
|> assign(:order, nil)
end
@impl true
def handle_event("delete", %{"id" => id}, socket) do
order = Orders.get_order!(id)
{:ok, _} = Orders.delete_order(order)
{:noreply, assign(socket, :orders, list_orders())}
end
defp list_orders do
Orders.list_orders()
end
end

View File

@@ -0,0 +1,61 @@
<h1>Listing Orders</h1>
<%= if @live_action in [:new, :edit] do %>
<.modal return_to={Routes.order_index_path(@socket, :index)}>
<.live_component
module={OsuuspuutarhaWeb.OrderLive.FormComponent}
id={@order.id || :new}
title={@page_title}
action={@live_action}
order={@order}
return_to={Routes.order_index_path(@socket, :index)}
/>
</.modal>
<% end %>
<table>
<thead>
<tr>
<th>Tilausmuoto</th>
<th>Jakopaikka</th>
<th>Etunimi</th>
<th>Sukunimi</th>
<th>Osoite</th>
<th>Postinum.</th>
<th>T. paikka</th>
<th>Puh.</th>
<th>Säpö</th>
<th>Jäsen?</th>
<th>Kaksi laskua?</th>
<th>Parilliset viikot</th>
<th></th>
</tr>
</thead>
<tbody id="orders">
<%= for order <- @orders do %>
<tr id={"order-#{order.id}"}>
<td><%= order.order_type %></td>
<td><%= order.location %></td>
<td><%= order.fname %></td>
<td><%= order.lname %></td>
<td><%= order.address %></td>
<td><%= order.pcode %></td>
<td><%= order.city %></td>
<td><%= order.phone %></td>
<td><%= order.email %></td>
<td><%= order.is_member %></td>
<td><%= order.split_invoice %></td>
<td><%= order.even_weeks %></td>
<td>
<span><%= live_redirect "Näytä", to: Routes.order_show_path(@socket, :show, order) %></span>
<span><%= live_patch "Muokkaa", to: Routes.order_index_path(@socket, :edit, order) %></span>
<span><%= link "Poista", to: "#", phx_click: "delete", phx_value_id: order.id, data: [confirm: "Oletko varma?"] %></span>
</td>
</tr>
<% end %>
</tbody>
</table>
<span><%= live_patch "Lisää tilaus", to: Routes.order_index_path(@socket, :new) %></span>

View File

@@ -0,0 +1,21 @@
defmodule OsuuspuutarhaWeb.OrderLive.Show do
use OsuuspuutarhaWeb, :live_view
alias Osuuspuutarha.Orders
@impl true
def mount(_params, _session, socket) do
{:ok, socket}
end
@impl true
def handle_params(%{"id" => id}, _, socket) do
{:noreply,
socket
|> assign(:page_title, page_title(socket.assigns.live_action))
|> assign(:order, Orders.get_order!(id))}
end
defp page_title(:show), do: "Tilaukset tiedot"
defp page_title(:edit), do: "Muokkaa tilausta"
end

View File

@@ -0,0 +1,81 @@
<h1>Tilauksen tiedot</h1>
<%= if @live_action in [:edit] do %>
<.modal return_to={Routes.order_show_path(@socket, :show, @order)}>
<.live_component
module={OsuuspuutarhaWeb.OrderLive.FormComponent}
id={@order.id}
title={@page_title}
action={@live_action}
order={@order}
return_to={Routes.order_show_path(@socket, :show, @order)}
/>
</.modal>
<% end %>
<ul>
<li>
<strong>Tilausmuoto:</strong>
<%= @order.order_type %>
</li>
<li>
<strong>Jakopaikka:</strong>
<%= @order.location %>
</li>
<li>
<strong>Etunimi:</strong>
<%= @order.fname %>
</li>
<li>
<strong>Sukunimi:</strong>
<%= @order.lname %>
</li>
<li>
<strong>Osoite:</strong>
<%= @order.address %>
</li>
<li>
<strong>Postinumero:</strong>
<%= @order.pcode %>
</li>
<li>
<strong>Postitoimipaikka:</strong>
<%= @order.city %>
</li>
<li>
<strong>Puhelinnumero:</strong>
<%= @order.phone %>
</li>
<li>
<strong>Sähköpostiosoite:</strong>
<%= @order.email %>
</li>
<li>
<strong>Jäsen?</strong>
<%= @order.is_member %>
</li>
<li>
<strong>Kahdella laskulla:</strong>
<%= @order.split_invoice %>
</li>
<li>
<strong>Parilliset viikot:</strong>
<%= @order.even_weeks %>
</li>
</ul>
<span><%= live_patch "Muokkaa", to: Routes.order_show_path(@socket, :edit, @order), class: "button" %></span> |
<span><%= live_redirect "Takaisin", to: Routes.order_index_path(@socket, :index) %></span>

View File

@@ -18,6 +18,13 @@ defmodule OsuuspuutarhaWeb.Router do
pipe_through :browser pipe_through :browser
get "/", PageController, :index get "/", PageController, :index
live "/orders", OrderLive.Index, :index
live "/orders/new", OrderLive.Index, :new
live "/orders/:id/edit", OrderLive.Index, :edit
live "/orders/:id", OrderLive.Show, :show
live "/orders/:id/show/edit", OrderLive.Show, :edit
end end
# Other scopes may use custom stacks. # Other scopes may use custom stacks.

View File

@@ -0,0 +1,22 @@
defmodule Osuuspuutarha.Repo.Migrations.CreateOrders do
use Ecto.Migration
def change do
create table(:orders) do
add :order_type, :string
add :location, :string
add :fname, :string
add :lname, :string
add :address, :string
add :pcode, :string
add :city, :string
add :phone, :string
add :email, :string
add :is_member, :boolean, default: false, null: false
add :split_invoice, :boolean, default: false, null: false
add :even_weeks, :boolean, default: false, null: false
timestamps()
end
end
end

View File

@@ -0,0 +1,81 @@
defmodule Osuuspuutarha.OrdersTest do
use Osuuspuutarha.DataCase
alias Osuuspuutarha.Orders
describe "orders" do
alias Osuuspuutarha.Orders.Order
import Osuuspuutarha.OrdersFixtures
@invalid_attrs %{address: nil, city: nil, email: nil, even_weeks: nil, fname: nil, is_member: nil, lname: nil, location: nil, order_type: nil, pcode: nil, phone: nil, split_invoice: nil}
test "list_orders/0 returns all orders" do
order = order_fixture()
assert Orders.list_orders() == [order]
end
test "get_order!/1 returns the order with given id" do
order = order_fixture()
assert Orders.get_order!(order.id) == order
end
test "create_order/1 with valid data creates a order" do
valid_attrs = %{address: "some address", city: "some city", email: "some email", even_weeks: true, fname: "some fname", is_member: true, lname: "some lname", location: :koroinen, order_type: :full, pcode: "some pcode", phone: "some phone", split_invoice: true}
assert {:ok, %Order{} = order} = Orders.create_order(valid_attrs)
assert order.address == "some address"
assert order.city == "some city"
assert order.email == "some email"
assert order.even_weeks == true
assert order.fname == "some fname"
assert order.is_member == true
assert order.lname == "some lname"
assert order.location == :koroinen
assert order.order_type == :full
assert order.pcode == "some pcode"
assert order.phone == "some phone"
assert order.split_invoice == true
end
test "create_order/1 with invalid data returns error changeset" do
assert {:error, %Ecto.Changeset{}} = Orders.create_order(@invalid_attrs)
end
test "update_order/2 with valid data updates the order" do
order = order_fixture()
update_attrs = %{address: "some updated address", city: "some updated city", email: "some updated email", even_weeks: false, fname: "some updated fname", is_member: false, lname: "some updated lname", location: :ocean, order_type: :everyother, pcode: "some updated pcode", phone: "some updated phone", split_invoice: false}
assert {:ok, %Order{} = order} = Orders.update_order(order, update_attrs)
assert order.address == "some updated address"
assert order.city == "some updated city"
assert order.email == "some updated email"
assert order.even_weeks == false
assert order.fname == "some updated fname"
assert order.is_member == false
assert order.lname == "some updated lname"
assert order.location == :ocean
assert order.order_type == :everyother
assert order.pcode == "some updated pcode"
assert order.phone == "some updated phone"
assert order.split_invoice == false
end
test "update_order/2 with invalid data returns error changeset" do
order = order_fixture()
assert {:error, %Ecto.Changeset{}} = Orders.update_order(order, @invalid_attrs)
assert order == Orders.get_order!(order.id)
end
test "delete_order/1 deletes the order" do
order = order_fixture()
assert {:ok, %Order{}} = Orders.delete_order(order)
assert_raise Ecto.NoResultsError, fn -> Orders.get_order!(order.id) end
end
test "change_order/1 returns a order changeset" do
order = order_fixture()
assert %Ecto.Changeset{} = Orders.change_order(order)
end
end
end

View File

@@ -0,0 +1,110 @@
defmodule OsuuspuutarhaWeb.OrderLiveTest do
use OsuuspuutarhaWeb.ConnCase
import Phoenix.LiveViewTest
import Osuuspuutarha.OrdersFixtures
@create_attrs %{address: "some address", city: "some city", email: "some email", even_weeks: true, fname: "some fname", is_member: true, lname: "some lname", location: :koroinen, order_type: :full, pcode: "some pcode", phone: "some phone", split_invoice: true}
@update_attrs %{address: "some updated address", city: "some updated city", email: "some updated email", even_weeks: false, fname: "some updated fname", is_member: false, lname: "some updated lname", location: :ocean, order_type: :everyother, pcode: "some updated pcode", phone: "some updated phone", split_invoice: false}
@invalid_attrs %{address: nil, city: nil, email: nil, even_weeks: false, fname: nil, is_member: false, lname: nil, location: nil, order_type: nil, pcode: nil, phone: nil, split_invoice: false}
defp create_order(_) do
order = order_fixture()
%{order: order}
end
describe "Index" do
setup [:create_order]
test "lists all orders", %{conn: conn, order: order} do
{:ok, _index_live, html} = live(conn, Routes.order_index_path(conn, :index))
assert html =~ "Listing Orders"
assert html =~ order.address
end
test "saves new order", %{conn: conn} do
{:ok, index_live, _html} = live(conn, Routes.order_index_path(conn, :index))
assert index_live |> element("a", "New Order") |> render_click() =~
"New Order"
assert_patch(index_live, Routes.order_index_path(conn, :new))
assert index_live
|> form("#order-form", order: @invalid_attrs)
|> render_change() =~ "can&#39;t be blank"
{:ok, _, html} =
index_live
|> form("#order-form", order: @create_attrs)
|> render_submit()
|> follow_redirect(conn, Routes.order_index_path(conn, :index))
assert html =~ "Order created successfully"
assert html =~ "some address"
end
test "updates order in listing", %{conn: conn, order: order} do
{:ok, index_live, _html} = live(conn, Routes.order_index_path(conn, :index))
assert index_live |> element("#order-#{order.id} a", "Edit") |> render_click() =~
"Edit Order"
assert_patch(index_live, Routes.order_index_path(conn, :edit, order))
assert index_live
|> form("#order-form", order: @invalid_attrs)
|> render_change() =~ "can&#39;t be blank"
{:ok, _, html} =
index_live
|> form("#order-form", order: @update_attrs)
|> render_submit()
|> follow_redirect(conn, Routes.order_index_path(conn, :index))
assert html =~ "Order updated successfully"
assert html =~ "some updated address"
end
test "deletes order in listing", %{conn: conn, order: order} do
{:ok, index_live, _html} = live(conn, Routes.order_index_path(conn, :index))
assert index_live |> element("#order-#{order.id} a", "Delete") |> render_click()
refute has_element?(index_live, "#order-#{order.id}")
end
end
describe "Show" do
setup [:create_order]
test "displays order", %{conn: conn, order: order} do
{:ok, _show_live, html} = live(conn, Routes.order_show_path(conn, :show, order))
assert html =~ "Show Order"
assert html =~ order.address
end
test "updates order within modal", %{conn: conn, order: order} do
{:ok, show_live, _html} = live(conn, Routes.order_show_path(conn, :show, order))
assert show_live |> element("a", "Edit") |> render_click() =~
"Edit Order"
assert_patch(show_live, Routes.order_show_path(conn, :edit, order))
assert show_live
|> form("#order-form", order: @invalid_attrs)
|> render_change() =~ "can&#39;t be blank"
{:ok, _, html} =
show_live
|> form("#order-form", order: @update_attrs)
|> render_submit()
|> follow_redirect(conn, Routes.order_show_path(conn, :show, order))
assert html =~ "Order updated successfully"
assert html =~ "some updated address"
end
end
end

View File

@@ -0,0 +1,31 @@
defmodule Osuuspuutarha.OrdersFixtures do
@moduledoc """
This module defines test helpers for creating
entities via the `Osuuspuutarha.Orders` context.
"""
@doc """
Generate a order.
"""
def order_fixture(attrs \\ %{}) do
{:ok, order} =
attrs
|> Enum.into(%{
address: "some address",
city: "some city",
email: "some email",
even_weeks: true,
fname: "some fname",
is_member: true,
lname: "some lname",
location: :koroinen,
order_type: :full,
pcode: "some pcode",
phone: "some phone",
split_invoice: true
})
|> Osuuspuutarha.Orders.create_order()
order
end
end