CTKOB4AB6TW2J7REZYGA76IPE56EHVCS6PX6VRV5YUH6LPSWKJTQC
defmodule Cake.BoardsFixtures do
@moduledoc """
This module defines test helpers for creating
entities via the `Cake.Boards` context.
"""
@doc """
Generate a board.
"""
def board_fixture(attrs \\ %{}) do
{:ok, board} =
attrs
|> Enum.into(%{
name: "some name",
stories: []
})
|> Cake.Boards.create_board()
board
end
end
defmodule CakeWeb.BoardLiveTest do
use CakeWeb.ConnCase
import Phoenix.LiveViewTest
import Cake.BoardsFixtures
@create_attrs %{name: "some name", stories: []}
@update_attrs %{name: "some updated name", stories: []}
@invalid_attrs %{name: nil, stories: []}
defp create_board(_) do
board = board_fixture()
%{board: board}
end
describe "Index" do
setup [:create_board]
test "lists all boards", %{conn: conn, board: board} do
{:ok, _index_live, html} = live(conn, Routes.board_index_path(conn, :index))
assert html =~ "Listing Boards"
assert html =~ board.name
end
test "saves new board", %{conn: conn} do
{:ok, index_live, _html} = live(conn, Routes.board_index_path(conn, :index))
assert index_live |> element("a", "New Board") |> render_click() =~
"New Board"
assert_patch(index_live, Routes.board_index_path(conn, :new))
assert index_live
|> form("#board-form", board: @invalid_attrs)
|> render_change() =~ "can't be blank"
{:ok, _, html} =
index_live
|> form("#board-form", board: @create_attrs)
|> render_submit()
|> follow_redirect(conn, Routes.board_index_path(conn, :index))
assert html =~ "Board created successfully"
assert html =~ "some name"
end
test "updates board in listing", %{conn: conn, board: board} do
{:ok, index_live, _html} = live(conn, Routes.board_index_path(conn, :index))
assert index_live |> element("#board-#{board.id} a", "Edit") |> render_click() =~
"Edit Board"
assert_patch(index_live, Routes.board_index_path(conn, :edit, board))
assert index_live
|> form("#board-form", board: @invalid_attrs)
|> render_change() =~ "can't be blank"
{:ok, _, html} =
index_live
|> form("#board-form", board: @update_attrs)
|> render_submit()
|> follow_redirect(conn, Routes.board_index_path(conn, :index))
assert html =~ "Board updated successfully"
assert html =~ "some updated name"
end
test "deletes board in listing", %{conn: conn, board: board} do
{:ok, index_live, _html} = live(conn, Routes.board_index_path(conn, :index))
assert index_live |> element("#board-#{board.id} a", "Delete") |> render_click()
refute has_element?(index_live, "#board-#{board.id}")
end
end
describe "Show" do
setup [:create_board]
test "displays board", %{conn: conn, board: board} do
{:ok, _show_live, html} = live(conn, Routes.board_show_path(conn, :show, board))
assert html =~ "Show Board"
assert html =~ board.name
end
test "updates board within modal", %{conn: conn, board: board} do
{:ok, show_live, _html} = live(conn, Routes.board_show_path(conn, :show, board))
assert show_live |> element("a", "Edit") |> render_click() =~
"Edit Board"
assert_patch(show_live, Routes.board_show_path(conn, :edit, board))
assert show_live
|> form("#board-form", board: @invalid_attrs)
|> render_change() =~ "can't be blank"
{:ok, _, html} =
show_live
|> form("#board-form", board: @update_attrs)
|> render_submit()
|> follow_redirect(conn, Routes.board_show_path(conn, :show, board))
assert html =~ "Board updated successfully"
assert html =~ "some updated name"
end
end
end
defmodule Cake.BoardsTest do
use Cake.DataCase
alias Cake.Boards
describe "boards" do
alias Cake.Boards.Board
import Cake.BoardsFixtures
@invalid_attrs %{name: nil, stories: nil}
test "list_boards/0 returns all boards" do
board = board_fixture()
assert Boards.list_boards() == [board]
end
test "get_board!/1 returns the board with given id" do
board = board_fixture()
assert Boards.get_board!(board.id) == board
end
test "create_board/1 with valid data creates a board" do
valid_attrs = %{name: "some name", stories: []}
assert {:ok, %Board{} = board} = Boards.create_board(valid_attrs)
assert board.name == "some name"
assert board.stories == []
end
test "create_board/1 with invalid data returns error changeset" do
assert {:error, %Ecto.Changeset{}} = Boards.create_board(@invalid_attrs)
end
test "update_board/2 with valid data updates the board" do
board = board_fixture()
update_attrs = %{name: "some updated name", stories: []}
assert {:ok, %Board{} = board} = Boards.update_board(board, update_attrs)
assert board.name == "some updated name"
assert board.stories == []
end
test "update_board/2 with invalid data returns error changeset" do
board = board_fixture()
assert {:error, %Ecto.Changeset{}} = Boards.update_board(board, @invalid_attrs)
assert board == Boards.get_board!(board.id)
end
test "delete_board/1 deletes the board" do
board = board_fixture()
assert {:ok, %Board{}} = Boards.delete_board(board)
assert_raise Ecto.NoResultsError, fn -> Boards.get_board!(board.id) end
end
test "change_board/1 returns a board changeset" do
board = board_fixture()
assert %Ecto.Changeset{} = Boards.change_board(board)
end
end
end
defmodule Cake.Repo.Migrations.CreateBoards do
use Ecto.Migration
def change do
create table(:boards, primary_key: false) do
add :id, :uuid, primary_key: true, null: false, default: fragment("gen_random_uuid()")
add :name, :string
add :stories, {:array, :uuid}
add :layer_template, references(:layer_templates, on_delete: :nothing)
timestamps()
end
create index(:boards, [:layer_template])
end
end
defmodule CakeWeb.Router do
use CakeWeb, :router
import Surface.Catalogue.Router
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_live_flash
plug :put_root_layout, {CakeWeb.LayoutView, :root}
plug :protect_from_forgery
plug :put_secure_browser_headers
end
pipeline :api do
plug :accepts, ["json"]
end
scope "/", CakeWeb do
pipe_through :browser
get "/", PageController, :index
end
# Other scopes may use custom stacks.
# scope "/api", CakeWeb do
# pipe_through :api
# end
# Enables LiveDashboard only for development
#
# If you want to use the LiveDashboard in production, you should put
# it behind authentication and allow only admins to access it.
# If your application does not have an admins-only section yet,
# you can use Plug.BasicAuth to set up some basic authentication
# as long as you are also using SSL (which you should anyway).
if Mix.env() in [:dev, :test] do
import Phoenix.LiveDashboard.Router
scope "/" do
pipe_through :browser
live_dashboard "/dashboard", metrics: CakeWeb.Telemetry
end
end
# Enables the Swoosh mailbox preview in development.
#
# Note that preview only shows emails that were sent by the same
# node running the Phoenix server.
if Mix.env() == :dev do
scope "/dev" do
pipe_through :browser
forward "/mailbox", Plug.Swoosh.MailboxPreview
end
end
if Mix.env() == :dev do
scope "/" do
pipe_through :browser
surface_catalogue "/catalogue"
end
end
end
<h1>Show Board</h1>
<%= if @live_action in [:edit] do %>
<.modal return_to={Routes.board_show_path(@socket, :show, @board)}>
<.live_component
module={CakeWeb.BoardLive.FormComponent}
id={@board.id}
title={@page_title}
action={@live_action}
board={@board}
return_to={Routes.board_show_path(@socket, :show, @board)}
/>
</.modal>
<% end %>
<ul>
<li>
<strong>Name:</strong>
<%= @board.name %>
</li>
<li>
<strong>Stories:</strong>
<%= @board.stories %>
</li>
</ul>
<span><%= live_patch "Edit", to: Routes.board_show_path(@socket, :edit, @board), class: "button" %></span> |
<span><%= live_redirect "Back", to: Routes.board_index_path(@socket, :index) %></span>
defmodule CakeWeb.BoardLive.Show do
use CakeWeb, :live_view
alias Cake.Boards
@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(:board, Boards.get_board!(id))}
end
defp page_title(:show), do: "Show Board"
defp page_title(:edit), do: "Edit Board"
end
<h1>Listing Boards</h1>
<%= if @live_action in [:new, :edit] do %>
<.modal return_to={Routes.board_index_path(@socket, :index)}>
<.live_component
module={CakeWeb.BoardLive.FormComponent}
id={@board.id || :new}
title={@page_title}
action={@live_action}
board={@board}
return_to={Routes.board_index_path(@socket, :index)}
/>
</.modal>
<% end %>
<table>
<thead>
<tr>
<th>Name</th>
<th>Stories</th>
<th></th>
</tr>
</thead>
<tbody id="boards">
<%= for board <- @boards do %>
<tr id={"board-#{board.id}"}>
<td><%= board.name %></td>
<td><%= board.stories %></td>
<td>
<span><%= live_redirect "Show", to: Routes.board_show_path(@socket, :show, board) %></span>
<span><%= live_patch "Edit", to: Routes.board_index_path(@socket, :edit, board) %></span>
<span><%= link "Delete", to: "#", phx_click: "delete", phx_value_id: board.id, data: [confirm: "Are you sure?"] %></span>
</td>
</tr>
<% end %>
</tbody>
</table>
<span><%= live_patch "New Board", to: Routes.board_index_path(@socket, :new) %></span>
defmodule CakeWeb.BoardLive.Index do
use CakeWeb, :live_view
alias Cake.Boards
alias Cake.Boards.Board
@impl true
def mount(_params, _session, socket) do
{:ok, assign(socket, :boards, list_boards())}
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, "Edit Board")
|> assign(:board, Boards.get_board!(id))
end
defp apply_action(socket, :new, _params) do
socket
|> assign(:page_title, "New Board")
|> assign(:board, %Board{})
end
defp apply_action(socket, :index, _params) do
socket
|> assign(:page_title, "Listing Boards")
|> assign(:board, nil)
end
@impl true
def handle_event("delete", %{"id" => id}, socket) do
board = Boards.get_board!(id)
{:ok, _} = Boards.delete_board(board)
{:noreply, assign(socket, :boards, list_boards())}
end
defp list_boards do
Boards.list_boards()
end
end
<div>
<h2><%= @title %></h2>
<.form
let={f}
for={@changeset}
id="board-form"
phx-target={@myself}
phx-change="validate"
phx-submit="save">
<%= label f, :name %>
<%= text_input f, :name %>
<%= error_tag f, :name %>
<%= label f, :stories %>
<%= multiple_select f, :stories, ["Option 1": "option1", "Option 2": "option2"] %>
<%= error_tag f, :stories %>
<div>
<%= submit "Save", phx_disable_with: "Saving..." %>
</div>
</.form>
</div>
defmodule CakeWeb.BoardLive.FormComponent do
use CakeWeb, :live_component
alias Cake.Boards
@impl true
def update(%{board: board} = assigns, socket) do
changeset = Boards.change_board(board)
{:ok,
socket
|> assign(assigns)
|> assign(:changeset, changeset)}
end
@impl true
def handle_event("validate", %{"board" => board_params}, socket) do
changeset =
socket.assigns.board
|> Boards.change_board(board_params)
|> Map.put(:action, :validate)
{:noreply, assign(socket, :changeset, changeset)}
end
def handle_event("save", %{"board" => board_params}, socket) do
save_board(socket, socket.assigns.action, board_params)
end
defp save_board(socket, :edit, board_params) do
case Boards.update_board(socket.assigns.board, board_params) do
{:ok, _board} ->
{:noreply,
socket
|> put_flash(:info, "Board updated successfully")
|> push_redirect(to: socket.assigns.return_to)}
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, :changeset, changeset)}
end
end
defp save_board(socket, :new, board_params) do
case Boards.create_board(board_params) do
{:ok, _board} ->
{:noreply,
socket
|> put_flash(:info, "Board created successfully")
|> push_redirect(to: socket.assigns.return_to)}
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, changeset: changeset)}
end
end
end
defmodule Cake.Boards do
@moduledoc """
The Boards context.
"""
import Ecto.Query, warn: false
alias Cake.Repo
alias Cake.Boards.Board
@doc """
Returns the list of boards.
## Examples
iex> list_boards()
[%Board{}, ...]
"""
def list_boards do
Repo.all(Board)
end
@doc """
Gets a single board.
Raises `Ecto.NoResultsError` if the Board does not exist.
## Examples
iex> get_board!(123)
%Board{}
iex> get_board!(456)
** (Ecto.NoResultsError)
"""
def get_board!(id), do: Repo.get!(Board, id)
@doc """
Creates a board.
## Examples
iex> create_board(%{field: value})
{:ok, %Board{}}
iex> create_board(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_board(attrs \\ %{}) do
%Board{}
|> Board.changeset(attrs)
|> Repo.insert()
end
@doc """
Updates a board.
## Examples
iex> update_board(board, %{field: new_value})
{:ok, %Board{}}
iex> update_board(board, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def update_board(%Board{} = board, attrs) do
board
|> Board.changeset(attrs)
|> Repo.update()
end
@doc """
Deletes a board.
## Examples
iex> delete_board(board)
{:ok, %Board{}}
iex> delete_board(board)
{:error, %Ecto.Changeset{}}
"""
def delete_board(%Board{} = board) do
Repo.delete(board)
end
@doc """
Returns an `%Ecto.Changeset{}` for tracking board changes.
## Examples
iex> change_board(board)
%Ecto.Changeset{data: %Board{}}
"""
def change_board(%Board{} = board, attrs \\ %{}) do
Board.changeset(board, attrs)
end
end
defmodule Cake.Boards.Board do
use Ecto.Schema
import Ecto.Changeset
schema "boards" do
field :name, :string
field :stories, {:array, Ecto.UUID}
field :layer_template, :id
timestamps()
end
@doc false
def changeset(board, attrs) do
board
|> cast(attrs, [:name, :stories])
|> validate_required([:name, :stories])
end
end