CRUD
This commit is contained in:
parent
0f48fd8e2e
commit
702e36c88f
29 changed files with 1707 additions and 0 deletions
296
lib/homeman/accounts.ex
Normal file
296
lib/homeman/accounts.ex
Normal file
|
@ -0,0 +1,296 @@
|
|||
defmodule Homeman.Accounts do
|
||||
@moduledoc """
|
||||
The Accounts context.
|
||||
"""
|
||||
|
||||
import Ecto.Query, warn: false
|
||||
alias Homeman.Repo
|
||||
|
||||
alias Homeman.Accounts.User
|
||||
|
||||
@doc """
|
||||
Returns the list of users.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> list_users()
|
||||
[%User{}, ...]
|
||||
|
||||
"""
|
||||
def list_users do
|
||||
Repo.all(User)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets a single user.
|
||||
|
||||
Raises `Ecto.NoResultsError` if the User does not exist.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> get_user!(123)
|
||||
%User{}
|
||||
|
||||
iex> get_user!(456)
|
||||
** (Ecto.NoResultsError)
|
||||
|
||||
"""
|
||||
def get_user!(id), do: Repo.get!(User, id)
|
||||
|
||||
@doc """
|
||||
Creates a user.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> create_user(%{field: value})
|
||||
{:ok, %User{}}
|
||||
|
||||
iex> create_user(%{field: bad_value})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def create_user(attrs \\ %{}) do
|
||||
%User{}
|
||||
|> User.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates a user.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> update_user(user, %{field: new_value})
|
||||
{:ok, %User{}}
|
||||
|
||||
iex> update_user(user, %{field: bad_value})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def update_user(%User{} = user, attrs) do
|
||||
user
|
||||
|> User.changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes a user.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> delete_user(user)
|
||||
{:ok, %User{}}
|
||||
|
||||
iex> delete_user(user)
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def delete_user(%User{} = user) do
|
||||
Repo.delete(user)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns an `%Ecto.Changeset{}` for tracking user changes.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> change_user(user)
|
||||
%Ecto.Changeset{data: %User{}}
|
||||
|
||||
"""
|
||||
def change_user(%User{} = user, attrs \\ %{}) do
|
||||
User.changeset(user, attrs)
|
||||
end
|
||||
|
||||
alias Homeman.Accounts.Todo
|
||||
|
||||
@doc """
|
||||
Returns the list of todos.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> list_todos()
|
||||
[%Todo{}, ...]
|
||||
|
||||
"""
|
||||
def list_todos do
|
||||
Repo.all(Todo)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets a single todo.
|
||||
|
||||
Raises `Ecto.NoResultsError` if the Todo does not exist.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> get_todo!(123)
|
||||
%Todo{}
|
||||
|
||||
iex> get_todo!(456)
|
||||
** (Ecto.NoResultsError)
|
||||
|
||||
"""
|
||||
def get_todo!(id), do: Repo.get!(Todo, id)
|
||||
|
||||
@doc """
|
||||
Creates a todo.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> create_todo(%{field: value})
|
||||
{:ok, %Todo{}}
|
||||
|
||||
iex> create_todo(%{field: bad_value})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def create_todo(attrs \\ %{}) do
|
||||
%Todo{}
|
||||
|> Todo.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates a todo.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> update_todo(todo, %{field: new_value})
|
||||
{:ok, %Todo{}}
|
||||
|
||||
iex> update_todo(todo, %{field: bad_value})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def update_todo(%Todo{} = todo, attrs) do
|
||||
todo
|
||||
|> Todo.changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes a todo.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> delete_todo(todo)
|
||||
{:ok, %Todo{}}
|
||||
|
||||
iex> delete_todo(todo)
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def delete_todo(%Todo{} = todo) do
|
||||
Repo.delete(todo)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns an `%Ecto.Changeset{}` for tracking todo changes.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> change_todo(todo)
|
||||
%Ecto.Changeset{data: %Todo{}}
|
||||
|
||||
"""
|
||||
def change_todo(%Todo{} = todo, attrs \\ %{}) do
|
||||
Todo.changeset(todo, attrs)
|
||||
end
|
||||
|
||||
alias Homeman.Accounts.Task
|
||||
|
||||
@doc """
|
||||
Returns the list of tasks.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> list_tasks()
|
||||
[%Task{}, ...]
|
||||
|
||||
"""
|
||||
def list_tasks do
|
||||
Repo.all(Task)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets a single task.
|
||||
|
||||
Raises `Ecto.NoResultsError` if the Task does not exist.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> get_task!(123)
|
||||
%Task{}
|
||||
|
||||
iex> get_task!(456)
|
||||
** (Ecto.NoResultsError)
|
||||
|
||||
"""
|
||||
def get_task!(id), do: Repo.get!(Task, id)
|
||||
|
||||
@doc """
|
||||
Creates a task.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> create_task(%{field: value})
|
||||
{:ok, %Task{}}
|
||||
|
||||
iex> create_task(%{field: bad_value})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def create_task(attrs \\ %{}) do
|
||||
%Task{}
|
||||
|> Task.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates a task.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> update_task(task, %{field: new_value})
|
||||
{:ok, %Task{}}
|
||||
|
||||
iex> update_task(task, %{field: bad_value})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def update_task(%Task{} = task, attrs) do
|
||||
task
|
||||
|> Task.changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes a task.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> delete_task(task)
|
||||
{:ok, %Task{}}
|
||||
|
||||
iex> delete_task(task)
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def delete_task(%Task{} = task) do
|
||||
Repo.delete(task)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns an `%Ecto.Changeset{}` for tracking task changes.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> change_task(task)
|
||||
%Ecto.Changeset{data: %Task{}}
|
||||
|
||||
"""
|
||||
def change_task(%Task{} = task, attrs \\ %{}) do
|
||||
Task.changeset(task, attrs)
|
||||
end
|
||||
end
|
22
lib/homeman/accounts/task.ex
Normal file
22
lib/homeman/accounts/task.ex
Normal file
|
@ -0,0 +1,22 @@
|
|||
defmodule Homeman.Accounts.Task do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@primary_key {:id, :binary_id, autogenerate: true}
|
||||
@foreign_key_type :binary_id
|
||||
schema "tasks" do
|
||||
field :description, :string
|
||||
field :phase, :string
|
||||
field :emoji, :string
|
||||
field :completed_at, :naive_datetime
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(task, attrs) do
|
||||
task
|
||||
|> cast(attrs, [:description, :emoji, :completed_at, :phase])
|
||||
|> validate_required([:description, :emoji, :phase])
|
||||
end
|
||||
end
|
22
lib/homeman/accounts/todo.ex
Normal file
22
lib/homeman/accounts/todo.ex
Normal file
|
@ -0,0 +1,22 @@
|
|||
defmodule Homeman.Accounts.Todo do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@primary_key {:id, :binary_id, autogenerate: true}
|
||||
@foreign_key_type :binary_id
|
||||
schema "todos" do
|
||||
field :description, :string
|
||||
field :emoji, :string
|
||||
field :completed_at, :naive_datetime
|
||||
field :assignee_user_id, :binary_id
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(todo, attrs) do
|
||||
todo
|
||||
|> cast(attrs, [:description, :emoji, :completed_at])
|
||||
|> validate_required([:description, :emoji, :completed_at])
|
||||
end
|
||||
end
|
21
lib/homeman/accounts/user.ex
Normal file
21
lib/homeman/accounts/user.ex
Normal file
|
@ -0,0 +1,21 @@
|
|||
defmodule Homeman.Accounts.User do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@primary_key {:id, :binary_id, autogenerate: true}
|
||||
@foreign_key_type :binary_id
|
||||
schema "users" do
|
||||
field :name, :string
|
||||
field :color, :string
|
||||
field :avatar_url, :string
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(user, attrs) do
|
||||
user
|
||||
|> cast(attrs, [:name, :avatar_url, :color])
|
||||
|> validate_required([:name, :avatar_url, :color])
|
||||
end
|
||||
end
|
93
lib/homeman_web/live/task_live/form_component.ex
Normal file
93
lib/homeman_web/live/task_live/form_component.ex
Normal file
|
@ -0,0 +1,93 @@
|
|||
defmodule HomemanWeb.TaskLive.FormComponent do
|
||||
use HomemanWeb, :live_component
|
||||
|
||||
alias Homeman.Accounts
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div>
|
||||
<.header>
|
||||
<%= @title %>
|
||||
<:subtitle>Use this form to manage task records in your database.</:subtitle>
|
||||
</.header>
|
||||
|
||||
<.simple_form
|
||||
for={@form}
|
||||
id="task-form"
|
||||
phx-target={@myself}
|
||||
phx-change="validate"
|
||||
phx-submit="save"
|
||||
>
|
||||
<.input field={@form[:description]} type="text" label="Description" />
|
||||
<.input field={@form[:emoji]} type="text" label="Emoji" />
|
||||
<.input field={@form[:completed_at]} type="datetime-local" label="Completed at" />
|
||||
<.input field={@form[:phase]} type="text" label="Phase" />
|
||||
<:actions>
|
||||
<.button phx-disable-with="Saving...">Save Task</.button>
|
||||
</:actions>
|
||||
</.simple_form>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
@impl true
|
||||
def update(%{task: task} = assigns, socket) do
|
||||
changeset = Accounts.change_task(task)
|
||||
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(assigns)
|
||||
|> assign_form(changeset)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("validate", %{"task" => task_params}, socket) do
|
||||
changeset =
|
||||
socket.assigns.task
|
||||
|> Accounts.change_task(task_params)
|
||||
|> Map.put(:action, :validate)
|
||||
|
||||
{:noreply, assign_form(socket, changeset)}
|
||||
end
|
||||
|
||||
def handle_event("save", %{"task" => task_params}, socket) do
|
||||
save_task(socket, socket.assigns.action, task_params)
|
||||
end
|
||||
|
||||
defp save_task(socket, :edit, task_params) do
|
||||
case Accounts.update_task(socket.assigns.task, task_params) do
|
||||
{:ok, task} ->
|
||||
notify_parent({:saved, task})
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:info, "Task updated successfully")
|
||||
|> push_patch(to: socket.assigns.patch)}
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
{:noreply, assign_form(socket, changeset)}
|
||||
end
|
||||
end
|
||||
|
||||
defp save_task(socket, :new, task_params) do
|
||||
case Accounts.create_task(task_params) do
|
||||
{:ok, task} ->
|
||||
notify_parent({:saved, task})
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:info, "Task created successfully")
|
||||
|> push_patch(to: socket.assigns.patch)}
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
{:noreply, assign_form(socket, changeset)}
|
||||
end
|
||||
end
|
||||
|
||||
defp assign_form(socket, %Ecto.Changeset{} = changeset) do
|
||||
assign(socket, :form, to_form(changeset))
|
||||
end
|
||||
|
||||
defp notify_parent(msg), do: send(self(), {__MODULE__, msg})
|
||||
end
|
47
lib/homeman_web/live/task_live/index.ex
Normal file
47
lib/homeman_web/live/task_live/index.ex
Normal file
|
@ -0,0 +1,47 @@
|
|||
defmodule HomemanWeb.TaskLive.Index do
|
||||
use HomemanWeb, :live_view
|
||||
|
||||
alias Homeman.Accounts
|
||||
alias Homeman.Accounts.Task
|
||||
|
||||
@impl true
|
||||
def mount(_params, _session, socket) do
|
||||
{:ok, stream(socket, :tasks, Accounts.list_tasks())}
|
||||
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 Task")
|
||||
|> assign(:task, Accounts.get_task!(id))
|
||||
end
|
||||
|
||||
defp apply_action(socket, :new, _params) do
|
||||
socket
|
||||
|> assign(:page_title, "New Task")
|
||||
|> assign(:task, %Task{})
|
||||
end
|
||||
|
||||
defp apply_action(socket, :index, _params) do
|
||||
socket
|
||||
|> assign(:page_title, "Listing Tasks")
|
||||
|> assign(:task, nil)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info({HomemanWeb.TaskLive.FormComponent, {:saved, task}}, socket) do
|
||||
{:noreply, stream_insert(socket, :tasks, task)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("delete", %{"id" => id}, socket) do
|
||||
task = Accounts.get_task!(id)
|
||||
{:ok, _} = Accounts.delete_task(task)
|
||||
|
||||
{:noreply, stream_delete(socket, :tasks, task)}
|
||||
end
|
||||
end
|
44
lib/homeman_web/live/task_live/index.html.heex
Normal file
44
lib/homeman_web/live/task_live/index.html.heex
Normal file
|
@ -0,0 +1,44 @@
|
|||
<.header>
|
||||
Listing Tasks
|
||||
<:actions>
|
||||
<.link patch={~p"/tasks/new"}>
|
||||
<.button>New Task</.button>
|
||||
</.link>
|
||||
</:actions>
|
||||
</.header>
|
||||
|
||||
<.table
|
||||
id="tasks"
|
||||
rows={@streams.tasks}
|
||||
row_click={fn {_id, task} -> JS.navigate(~p"/tasks/#{task}") end}
|
||||
>
|
||||
<:col :let={{_id, task}} label="Description"><%= task.description %></:col>
|
||||
<:col :let={{_id, task}} label="Emoji"><%= task.emoji %></:col>
|
||||
<:col :let={{_id, task}} label="Completed at"><%= task.completed_at %></:col>
|
||||
<:col :let={{_id, task}} label="Phase"><%= task.phase %></:col>
|
||||
<:action :let={{_id, task}}>
|
||||
<div class="sr-only">
|
||||
<.link navigate={~p"/tasks/#{task}"}>Show</.link>
|
||||
</div>
|
||||
<.link patch={~p"/tasks/#{task}/edit"}>Edit</.link>
|
||||
</:action>
|
||||
<:action :let={{id, task}}>
|
||||
<.link
|
||||
phx-click={JS.push("delete", value: %{id: task.id}) |> hide("##{id}")}
|
||||
data-confirm="Are you sure?"
|
||||
>
|
||||
Delete
|
||||
</.link>
|
||||
</:action>
|
||||
</.table>
|
||||
|
||||
<.modal :if={@live_action in [:new, :edit]} id="task-modal" show on_cancel={JS.patch(~p"/tasks")}>
|
||||
<.live_component
|
||||
module={HomemanWeb.TaskLive.FormComponent}
|
||||
id={@task.id || :new}
|
||||
title={@page_title}
|
||||
action={@live_action}
|
||||
task={@task}
|
||||
patch={~p"/tasks"}
|
||||
/>
|
||||
</.modal>
|
21
lib/homeman_web/live/task_live/show.ex
Normal file
21
lib/homeman_web/live/task_live/show.ex
Normal file
|
@ -0,0 +1,21 @@
|
|||
defmodule HomemanWeb.TaskLive.Show do
|
||||
use HomemanWeb, :live_view
|
||||
|
||||
alias Homeman.Accounts
|
||||
|
||||
@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(:task, Accounts.get_task!(id))}
|
||||
end
|
||||
|
||||
defp page_title(:show), do: "Show Task"
|
||||
defp page_title(:edit), do: "Edit Task"
|
||||
end
|
29
lib/homeman_web/live/task_live/show.html.heex
Normal file
29
lib/homeman_web/live/task_live/show.html.heex
Normal file
|
@ -0,0 +1,29 @@
|
|||
<.header>
|
||||
Task <%= @task.id %>
|
||||
<:subtitle>This is a task record from your database.</:subtitle>
|
||||
<:actions>
|
||||
<.link patch={~p"/tasks/#{@task}/show/edit"} phx-click={JS.push_focus()}>
|
||||
<.button>Edit task</.button>
|
||||
</.link>
|
||||
</:actions>
|
||||
</.header>
|
||||
|
||||
<.list>
|
||||
<:item title="Description"><%= @task.description %></:item>
|
||||
<:item title="Emoji"><%= @task.emoji %></:item>
|
||||
<:item title="Completed at"><%= @task.completed_at %></:item>
|
||||
<:item title="Phase"><%= @task.phase %></:item>
|
||||
</.list>
|
||||
|
||||
<.back navigate={~p"/tasks"}>Back to tasks</.back>
|
||||
|
||||
<.modal :if={@live_action == :edit} id="task-modal" show on_cancel={JS.patch(~p"/tasks/#{@task}")}>
|
||||
<.live_component
|
||||
module={HomemanWeb.TaskLive.FormComponent}
|
||||
id={@task.id}
|
||||
title={@page_title}
|
||||
action={@live_action}
|
||||
task={@task}
|
||||
patch={~p"/tasks/#{@task}"}
|
||||
/>
|
||||
</.modal>
|
92
lib/homeman_web/live/todo_live/form_component.ex
Normal file
92
lib/homeman_web/live/todo_live/form_component.ex
Normal file
|
@ -0,0 +1,92 @@
|
|||
defmodule HomemanWeb.TodoLive.FormComponent do
|
||||
use HomemanWeb, :live_component
|
||||
|
||||
alias Homeman.Accounts
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div>
|
||||
<.header>
|
||||
<%= @title %>
|
||||
<:subtitle>Use this form to manage todo records in your database.</:subtitle>
|
||||
</.header>
|
||||
|
||||
<.simple_form
|
||||
for={@form}
|
||||
id="todo-form"
|
||||
phx-target={@myself}
|
||||
phx-change="validate"
|
||||
phx-submit="save"
|
||||
>
|
||||
<.input field={@form[:description]} type="text" label="Description" />
|
||||
<.input field={@form[:emoji]} type="text" label="Emoji" />
|
||||
<.input field={@form[:completed_at]} type="datetime-local" label="Completed at" />
|
||||
<:actions>
|
||||
<.button phx-disable-with="Saving...">Save Todo</.button>
|
||||
</:actions>
|
||||
</.simple_form>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
@impl true
|
||||
def update(%{todo: todo} = assigns, socket) do
|
||||
changeset = Accounts.change_todo(todo)
|
||||
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(assigns)
|
||||
|> assign_form(changeset)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("validate", %{"todo" => todo_params}, socket) do
|
||||
changeset =
|
||||
socket.assigns.todo
|
||||
|> Accounts.change_todo(todo_params)
|
||||
|> Map.put(:action, :validate)
|
||||
|
||||
{:noreply, assign_form(socket, changeset)}
|
||||
end
|
||||
|
||||
def handle_event("save", %{"todo" => todo_params}, socket) do
|
||||
save_todo(socket, socket.assigns.action, todo_params)
|
||||
end
|
||||
|
||||
defp save_todo(socket, :edit, todo_params) do
|
||||
case Accounts.update_todo(socket.assigns.todo, todo_params) do
|
||||
{:ok, todo} ->
|
||||
notify_parent({:saved, todo})
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:info, "Todo updated successfully")
|
||||
|> push_patch(to: socket.assigns.patch)}
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
{:noreply, assign_form(socket, changeset)}
|
||||
end
|
||||
end
|
||||
|
||||
defp save_todo(socket, :new, todo_params) do
|
||||
case Accounts.create_todo(todo_params) do
|
||||
{:ok, todo} ->
|
||||
notify_parent({:saved, todo})
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:info, "Todo created successfully")
|
||||
|> push_patch(to: socket.assigns.patch)}
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
{:noreply, assign_form(socket, changeset)}
|
||||
end
|
||||
end
|
||||
|
||||
defp assign_form(socket, %Ecto.Changeset{} = changeset) do
|
||||
assign(socket, :form, to_form(changeset))
|
||||
end
|
||||
|
||||
defp notify_parent(msg), do: send(self(), {__MODULE__, msg})
|
||||
end
|
47
lib/homeman_web/live/todo_live/index.ex
Normal file
47
lib/homeman_web/live/todo_live/index.ex
Normal file
|
@ -0,0 +1,47 @@
|
|||
defmodule HomemanWeb.TodoLive.Index do
|
||||
use HomemanWeb, :live_view
|
||||
|
||||
alias Homeman.Accounts
|
||||
alias Homeman.Accounts.Todo
|
||||
|
||||
@impl true
|
||||
def mount(_params, _session, socket) do
|
||||
{:ok, stream(socket, :todos, Accounts.list_todos())}
|
||||
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 Todo")
|
||||
|> assign(:todo, Accounts.get_todo!(id))
|
||||
end
|
||||
|
||||
defp apply_action(socket, :new, _params) do
|
||||
socket
|
||||
|> assign(:page_title, "New Todo")
|
||||
|> assign(:todo, %Todo{})
|
||||
end
|
||||
|
||||
defp apply_action(socket, :index, _params) do
|
||||
socket
|
||||
|> assign(:page_title, "Listing Todos")
|
||||
|> assign(:todo, nil)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info({HomemanWeb.TodoLive.FormComponent, {:saved, todo}}, socket) do
|
||||
{:noreply, stream_insert(socket, :todos, todo)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("delete", %{"id" => id}, socket) do
|
||||
todo = Accounts.get_todo!(id)
|
||||
{:ok, _} = Accounts.delete_todo(todo)
|
||||
|
||||
{:noreply, stream_delete(socket, :todos, todo)}
|
||||
end
|
||||
end
|
43
lib/homeman_web/live/todo_live/index.html.heex
Normal file
43
lib/homeman_web/live/todo_live/index.html.heex
Normal file
|
@ -0,0 +1,43 @@
|
|||
<.header>
|
||||
Listing Todos
|
||||
<:actions>
|
||||
<.link patch={~p"/todos/new"}>
|
||||
<.button>New Todo</.button>
|
||||
</.link>
|
||||
</:actions>
|
||||
</.header>
|
||||
|
||||
<.table
|
||||
id="todos"
|
||||
rows={@streams.todos}
|
||||
row_click={fn {_id, todo} -> JS.navigate(~p"/todos/#{todo}") end}
|
||||
>
|
||||
<:col :let={{_id, todo}} label="Description"><%= todo.description %></:col>
|
||||
<:col :let={{_id, todo}} label="Emoji"><%= todo.emoji %></:col>
|
||||
<:col :let={{_id, todo}} label="Completed at"><%= todo.completed_at %></:col>
|
||||
<:action :let={{_id, todo}}>
|
||||
<div class="sr-only">
|
||||
<.link navigate={~p"/todos/#{todo}"}>Show</.link>
|
||||
</div>
|
||||
<.link patch={~p"/todos/#{todo}/edit"}>Edit</.link>
|
||||
</:action>
|
||||
<:action :let={{id, todo}}>
|
||||
<.link
|
||||
phx-click={JS.push("delete", value: %{id: todo.id}) |> hide("##{id}")}
|
||||
data-confirm="Are you sure?"
|
||||
>
|
||||
Delete
|
||||
</.link>
|
||||
</:action>
|
||||
</.table>
|
||||
|
||||
<.modal :if={@live_action in [:new, :edit]} id="todo-modal" show on_cancel={JS.patch(~p"/todos")}>
|
||||
<.live_component
|
||||
module={HomemanWeb.TodoLive.FormComponent}
|
||||
id={@todo.id || :new}
|
||||
title={@page_title}
|
||||
action={@live_action}
|
||||
todo={@todo}
|
||||
patch={~p"/todos"}
|
||||
/>
|
||||
</.modal>
|
21
lib/homeman_web/live/todo_live/show.ex
Normal file
21
lib/homeman_web/live/todo_live/show.ex
Normal file
|
@ -0,0 +1,21 @@
|
|||
defmodule HomemanWeb.TodoLive.Show do
|
||||
use HomemanWeb, :live_view
|
||||
|
||||
alias Homeman.Accounts
|
||||
|
||||
@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(:todo, Accounts.get_todo!(id))}
|
||||
end
|
||||
|
||||
defp page_title(:show), do: "Show Todo"
|
||||
defp page_title(:edit), do: "Edit Todo"
|
||||
end
|
28
lib/homeman_web/live/todo_live/show.html.heex
Normal file
28
lib/homeman_web/live/todo_live/show.html.heex
Normal file
|
@ -0,0 +1,28 @@
|
|||
<.header>
|
||||
Todo <%= @todo.id %>
|
||||
<:subtitle>This is a todo record from your database.</:subtitle>
|
||||
<:actions>
|
||||
<.link patch={~p"/todos/#{@todo}/show/edit"} phx-click={JS.push_focus()}>
|
||||
<.button>Edit todo</.button>
|
||||
</.link>
|
||||
</:actions>
|
||||
</.header>
|
||||
|
||||
<.list>
|
||||
<:item title="Description"><%= @todo.description %></:item>
|
||||
<:item title="Emoji"><%= @todo.emoji %></:item>
|
||||
<:item title="Completed at"><%= @todo.completed_at %></:item>
|
||||
</.list>
|
||||
|
||||
<.back navigate={~p"/todos"}>Back to todos</.back>
|
||||
|
||||
<.modal :if={@live_action == :edit} id="todo-modal" show on_cancel={JS.patch(~p"/todos/#{@todo}")}>
|
||||
<.live_component
|
||||
module={HomemanWeb.TodoLive.FormComponent}
|
||||
id={@todo.id}
|
||||
title={@page_title}
|
||||
action={@live_action}
|
||||
todo={@todo}
|
||||
patch={~p"/todos/#{@todo}"}
|
||||
/>
|
||||
</.modal>
|
92
lib/homeman_web/live/user_live/form_component.ex
Normal file
92
lib/homeman_web/live/user_live/form_component.ex
Normal file
|
@ -0,0 +1,92 @@
|
|||
defmodule HomemanWeb.UserLive.FormComponent do
|
||||
use HomemanWeb, :live_component
|
||||
|
||||
alias Homeman.Accounts
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div>
|
||||
<.header>
|
||||
<%= @title %>
|
||||
<:subtitle>Use this form to manage user records in your database.</:subtitle>
|
||||
</.header>
|
||||
|
||||
<.simple_form
|
||||
for={@form}
|
||||
id="user-form"
|
||||
phx-target={@myself}
|
||||
phx-change="validate"
|
||||
phx-submit="save"
|
||||
>
|
||||
<.input field={@form[:name]} type="text" label="Name" />
|
||||
<.input field={@form[:avatar_url]} type="text" label="Avatar url" />
|
||||
<.input field={@form[:color]} type="text" label="Color" />
|
||||
<:actions>
|
||||
<.button phx-disable-with="Saving...">Save User</.button>
|
||||
</:actions>
|
||||
</.simple_form>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
@impl true
|
||||
def update(%{user: user} = assigns, socket) do
|
||||
changeset = Accounts.change_user(user)
|
||||
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(assigns)
|
||||
|> assign_form(changeset)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("validate", %{"user" => user_params}, socket) do
|
||||
changeset =
|
||||
socket.assigns.user
|
||||
|> Accounts.change_user(user_params)
|
||||
|> Map.put(:action, :validate)
|
||||
|
||||
{:noreply, assign_form(socket, changeset)}
|
||||
end
|
||||
|
||||
def handle_event("save", %{"user" => user_params}, socket) do
|
||||
save_user(socket, socket.assigns.action, user_params)
|
||||
end
|
||||
|
||||
defp save_user(socket, :edit, user_params) do
|
||||
case Accounts.update_user(socket.assigns.user, user_params) do
|
||||
{:ok, user} ->
|
||||
notify_parent({:saved, user})
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:info, "User updated successfully")
|
||||
|> push_patch(to: socket.assigns.patch)}
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
{:noreply, assign_form(socket, changeset)}
|
||||
end
|
||||
end
|
||||
|
||||
defp save_user(socket, :new, user_params) do
|
||||
case Accounts.create_user(user_params) do
|
||||
{:ok, user} ->
|
||||
notify_parent({:saved, user})
|
||||
|
||||
{:noreply,
|
||||
socket
|
||||
|> put_flash(:info, "User created successfully")
|
||||
|> push_patch(to: socket.assigns.patch)}
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
{:noreply, assign_form(socket, changeset)}
|
||||
end
|
||||
end
|
||||
|
||||
defp assign_form(socket, %Ecto.Changeset{} = changeset) do
|
||||
assign(socket, :form, to_form(changeset))
|
||||
end
|
||||
|
||||
defp notify_parent(msg), do: send(self(), {__MODULE__, msg})
|
||||
end
|
47
lib/homeman_web/live/user_live/index.ex
Normal file
47
lib/homeman_web/live/user_live/index.ex
Normal file
|
@ -0,0 +1,47 @@
|
|||
defmodule HomemanWeb.UserLive.Index do
|
||||
use HomemanWeb, :live_view
|
||||
|
||||
alias Homeman.Accounts
|
||||
alias Homeman.Accounts.User
|
||||
|
||||
@impl true
|
||||
def mount(_params, _session, socket) do
|
||||
{:ok, stream(socket, :users, Accounts.list_users())}
|
||||
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 User")
|
||||
|> assign(:user, Accounts.get_user!(id))
|
||||
end
|
||||
|
||||
defp apply_action(socket, :new, _params) do
|
||||
socket
|
||||
|> assign(:page_title, "New User")
|
||||
|> assign(:user, %User{})
|
||||
end
|
||||
|
||||
defp apply_action(socket, :index, _params) do
|
||||
socket
|
||||
|> assign(:page_title, "Listing Users")
|
||||
|> assign(:user, nil)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info({HomemanWeb.UserLive.FormComponent, {:saved, user}}, socket) do
|
||||
{:noreply, stream_insert(socket, :users, user)}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_event("delete", %{"id" => id}, socket) do
|
||||
user = Accounts.get_user!(id)
|
||||
{:ok, _} = Accounts.delete_user(user)
|
||||
|
||||
{:noreply, stream_delete(socket, :users, user)}
|
||||
end
|
||||
end
|
43
lib/homeman_web/live/user_live/index.html.heex
Normal file
43
lib/homeman_web/live/user_live/index.html.heex
Normal file
|
@ -0,0 +1,43 @@
|
|||
<.header>
|
||||
Listing Users
|
||||
<:actions>
|
||||
<.link patch={~p"/users/new"}>
|
||||
<.button>New User</.button>
|
||||
</.link>
|
||||
</:actions>
|
||||
</.header>
|
||||
|
||||
<.table
|
||||
id="users"
|
||||
rows={@streams.users}
|
||||
row_click={fn {_id, user} -> JS.navigate(~p"/users/#{user}") end}
|
||||
>
|
||||
<:col :let={{_id, user}} label="Name"><%= user.name %></:col>
|
||||
<:col :let={{_id, user}} label="Avatar url"><%= user.avatar_url %></:col>
|
||||
<:col :let={{_id, user}} label="Color"><%= user.color %></:col>
|
||||
<:action :let={{_id, user}}>
|
||||
<div class="sr-only">
|
||||
<.link navigate={~p"/users/#{user}"}>Show</.link>
|
||||
</div>
|
||||
<.link patch={~p"/users/#{user}/edit"}>Edit</.link>
|
||||
</:action>
|
||||
<:action :let={{id, user}}>
|
||||
<.link
|
||||
phx-click={JS.push("delete", value: %{id: user.id}) |> hide("##{id}")}
|
||||
data-confirm="Are you sure?"
|
||||
>
|
||||
Delete
|
||||
</.link>
|
||||
</:action>
|
||||
</.table>
|
||||
|
||||
<.modal :if={@live_action in [:new, :edit]} id="user-modal" show on_cancel={JS.patch(~p"/users")}>
|
||||
<.live_component
|
||||
module={HomemanWeb.UserLive.FormComponent}
|
||||
id={@user.id || :new}
|
||||
title={@page_title}
|
||||
action={@live_action}
|
||||
user={@user}
|
||||
patch={~p"/users"}
|
||||
/>
|
||||
</.modal>
|
21
lib/homeman_web/live/user_live/show.ex
Normal file
21
lib/homeman_web/live/user_live/show.ex
Normal file
|
@ -0,0 +1,21 @@
|
|||
defmodule HomemanWeb.UserLive.Show do
|
||||
use HomemanWeb, :live_view
|
||||
|
||||
alias Homeman.Accounts
|
||||
|
||||
@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(:user, Accounts.get_user!(id))}
|
||||
end
|
||||
|
||||
defp page_title(:show), do: "Show User"
|
||||
defp page_title(:edit), do: "Edit User"
|
||||
end
|
28
lib/homeman_web/live/user_live/show.html.heex
Normal file
28
lib/homeman_web/live/user_live/show.html.heex
Normal file
|
@ -0,0 +1,28 @@
|
|||
<.header>
|
||||
User <%= @user.id %>
|
||||
<:subtitle>This is a user record from your database.</:subtitle>
|
||||
<:actions>
|
||||
<.link patch={~p"/users/#{@user}/show/edit"} phx-click={JS.push_focus()}>
|
||||
<.button>Edit user</.button>
|
||||
</.link>
|
||||
</:actions>
|
||||
</.header>
|
||||
|
||||
<.list>
|
||||
<:item title="Name"><%= @user.name %></:item>
|
||||
<:item title="Avatar url"><%= @user.avatar_url %></:item>
|
||||
<:item title="Color"><%= @user.color %></:item>
|
||||
</.list>
|
||||
|
||||
<.back navigate={~p"/users"}>Back to users</.back>
|
||||
|
||||
<.modal :if={@live_action == :edit} id="user-modal" show on_cancel={JS.patch(~p"/users/#{@user}")}>
|
||||
<.live_component
|
||||
module={HomemanWeb.UserLive.FormComponent}
|
||||
id={@user.id}
|
||||
title={@page_title}
|
||||
action={@live_action}
|
||||
user={@user}
|
||||
patch={~p"/users/#{@user}"}
|
||||
/>
|
||||
</.modal>
|
|
@ -18,6 +18,29 @@ defmodule HomemanWeb.Router do
|
|||
pipe_through :browser
|
||||
|
||||
get "/", PageController, :home
|
||||
|
||||
live "/tasks", TaskLive.Index, :index
|
||||
live "/tasks/new", TaskLive.Index, :new
|
||||
live "/tasks/:id/edit", TaskLive.Index, :edit
|
||||
|
||||
live "/tasks/:id", TaskLive.Show, :show
|
||||
live "/tasks/:id/show/edit", TaskLive.Show, :edit
|
||||
|
||||
|
||||
live "/todos", TodoLive.Index, :index
|
||||
live "/todos/new", TodoLive.Index, :new
|
||||
live "/todos/:id/edit", TodoLive.Index, :edit
|
||||
|
||||
live "/todos/:id", TodoLive.Show, :show
|
||||
live "/todos/:id/show/edit", TodoLive.Show, :edit
|
||||
|
||||
|
||||
live "/users", UserLive.Index, :index
|
||||
live "/users/new", UserLive.Index, :new
|
||||
live "/users/:id/edit", UserLive.Index, :edit
|
||||
|
||||
live "/users/:id", UserLive.Show, :show
|
||||
live "/users/:id/show/edit", UserLive.Show, :edit
|
||||
end
|
||||
|
||||
# Other scopes may use custom stacks.
|
||||
|
|
14
priv/repo/migrations/20240122053553_create_users.exs
Normal file
14
priv/repo/migrations/20240122053553_create_users.exs
Normal file
|
@ -0,0 +1,14 @@
|
|||
defmodule Homeman.Repo.Migrations.CreateUsers do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:users, primary_key: false) do
|
||||
add :id, :binary_id, primary_key: true
|
||||
add :name, :string
|
||||
add :avatar_url, :string, null: true
|
||||
add :color, :string
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
end
|
||||
end
|
17
priv/repo/migrations/20240122054006_create_todos.exs
Normal file
17
priv/repo/migrations/20240122054006_create_todos.exs
Normal file
|
@ -0,0 +1,17 @@
|
|||
defmodule Homeman.Repo.Migrations.CreateTodos do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:todos, primary_key: false) do
|
||||
add :id, :binary_id, primary_key: true
|
||||
add :description, :string
|
||||
add :emoji, :string, null: true
|
||||
add :completed_at, :naive_datetime, null: true
|
||||
add :assignee_user_id, references(:users, on_delete: :nothing, type: :binary_id), null: true
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
|
||||
create index(:todos, [:assignee_user_id])
|
||||
end
|
||||
end
|
15
priv/repo/migrations/20240122054053_create_tasks.exs
Normal file
15
priv/repo/migrations/20240122054053_create_tasks.exs
Normal file
|
@ -0,0 +1,15 @@
|
|||
defmodule Homeman.Repo.Migrations.CreateTasks do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table(:tasks, primary_key: false) do
|
||||
add :id, :binary_id, primary_key: true
|
||||
add :description, :string
|
||||
add :emoji, :string, null: true
|
||||
add :completed_at, :naive_datetime, null: true
|
||||
add :phase, :string
|
||||
|
||||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -9,3 +9,9 @@
|
|||
#
|
||||
# We recommend using the bang functions (`insert!`, `update!`
|
||||
# and so on) as they will fail if something goes wrong.
|
||||
|
||||
%{
|
||||
name: "Daddy",
|
||||
color: "#00aaff",
|
||||
avatar_url: "/uploads/space.png",
|
||||
}
|
||||
|
|
181
test/homeman/accounts_test.exs
Normal file
181
test/homeman/accounts_test.exs
Normal file
|
@ -0,0 +1,181 @@
|
|||
defmodule Homeman.AccountsTest do
|
||||
use Homeman.DataCase
|
||||
|
||||
alias Homeman.Accounts
|
||||
|
||||
describe "users" do
|
||||
alias Homeman.Accounts.User
|
||||
|
||||
import Homeman.AccountsFixtures
|
||||
|
||||
@invalid_attrs %{name: nil, color: nil, avatar_url: nil}
|
||||
|
||||
test "list_users/0 returns all users" do
|
||||
user = user_fixture()
|
||||
assert Accounts.list_users() == [user]
|
||||
end
|
||||
|
||||
test "get_user!/1 returns the user with given id" do
|
||||
user = user_fixture()
|
||||
assert Accounts.get_user!(user.id) == user
|
||||
end
|
||||
|
||||
test "create_user/1 with valid data creates a user" do
|
||||
valid_attrs = %{name: "some name", color: "some color", avatar_url: "some avatar_url"}
|
||||
|
||||
assert {:ok, %User{} = user} = Accounts.create_user(valid_attrs)
|
||||
assert user.name == "some name"
|
||||
assert user.color == "some color"
|
||||
assert user.avatar_url == "some avatar_url"
|
||||
end
|
||||
|
||||
test "create_user/1 with invalid data returns error changeset" do
|
||||
assert {:error, %Ecto.Changeset{}} = Accounts.create_user(@invalid_attrs)
|
||||
end
|
||||
|
||||
test "update_user/2 with valid data updates the user" do
|
||||
user = user_fixture()
|
||||
update_attrs = %{name: "some updated name", color: "some updated color", avatar_url: "some updated avatar_url"}
|
||||
|
||||
assert {:ok, %User{} = user} = Accounts.update_user(user, update_attrs)
|
||||
assert user.name == "some updated name"
|
||||
assert user.color == "some updated color"
|
||||
assert user.avatar_url == "some updated avatar_url"
|
||||
end
|
||||
|
||||
test "update_user/2 with invalid data returns error changeset" do
|
||||
user = user_fixture()
|
||||
assert {:error, %Ecto.Changeset{}} = Accounts.update_user(user, @invalid_attrs)
|
||||
assert user == Accounts.get_user!(user.id)
|
||||
end
|
||||
|
||||
test "delete_user/1 deletes the user" do
|
||||
user = user_fixture()
|
||||
assert {:ok, %User{}} = Accounts.delete_user(user)
|
||||
assert_raise Ecto.NoResultsError, fn -> Accounts.get_user!(user.id) end
|
||||
end
|
||||
|
||||
test "change_user/1 returns a user changeset" do
|
||||
user = user_fixture()
|
||||
assert %Ecto.Changeset{} = Accounts.change_user(user)
|
||||
end
|
||||
end
|
||||
|
||||
describe "todos" do
|
||||
alias Homeman.Accounts.Todo
|
||||
|
||||
import Homeman.AccountsFixtures
|
||||
|
||||
@invalid_attrs %{description: nil, emoji: nil, completed_at: nil}
|
||||
|
||||
test "list_todos/0 returns all todos" do
|
||||
todo = todo_fixture()
|
||||
assert Accounts.list_todos() == [todo]
|
||||
end
|
||||
|
||||
test "get_todo!/1 returns the todo with given id" do
|
||||
todo = todo_fixture()
|
||||
assert Accounts.get_todo!(todo.id) == todo
|
||||
end
|
||||
|
||||
test "create_todo/1 with valid data creates a todo" do
|
||||
valid_attrs = %{description: "some description", emoji: "some emoji", completed_at: ~N[2024-01-21 05:40:00]}
|
||||
|
||||
assert {:ok, %Todo{} = todo} = Accounts.create_todo(valid_attrs)
|
||||
assert todo.description == "some description"
|
||||
assert todo.emoji == "some emoji"
|
||||
assert todo.completed_at == ~N[2024-01-21 05:40:00]
|
||||
end
|
||||
|
||||
test "create_todo/1 with invalid data returns error changeset" do
|
||||
assert {:error, %Ecto.Changeset{}} = Accounts.create_todo(@invalid_attrs)
|
||||
end
|
||||
|
||||
test "update_todo/2 with valid data updates the todo" do
|
||||
todo = todo_fixture()
|
||||
update_attrs = %{description: "some updated description", emoji: "some updated emoji", completed_at: ~N[2024-01-22 05:40:00]}
|
||||
|
||||
assert {:ok, %Todo{} = todo} = Accounts.update_todo(todo, update_attrs)
|
||||
assert todo.description == "some updated description"
|
||||
assert todo.emoji == "some updated emoji"
|
||||
assert todo.completed_at == ~N[2024-01-22 05:40:00]
|
||||
end
|
||||
|
||||
test "update_todo/2 with invalid data returns error changeset" do
|
||||
todo = todo_fixture()
|
||||
assert {:error, %Ecto.Changeset{}} = Accounts.update_todo(todo, @invalid_attrs)
|
||||
assert todo == Accounts.get_todo!(todo.id)
|
||||
end
|
||||
|
||||
test "delete_todo/1 deletes the todo" do
|
||||
todo = todo_fixture()
|
||||
assert {:ok, %Todo{}} = Accounts.delete_todo(todo)
|
||||
assert_raise Ecto.NoResultsError, fn -> Accounts.get_todo!(todo.id) end
|
||||
end
|
||||
|
||||
test "change_todo/1 returns a todo changeset" do
|
||||
todo = todo_fixture()
|
||||
assert %Ecto.Changeset{} = Accounts.change_todo(todo)
|
||||
end
|
||||
end
|
||||
|
||||
describe "tasks" do
|
||||
alias Homeman.Accounts.Task
|
||||
|
||||
import Homeman.AccountsFixtures
|
||||
|
||||
@invalid_attrs %{description: nil, phase: nil, emoji: nil, completed_at: nil}
|
||||
|
||||
test "list_tasks/0 returns all tasks" do
|
||||
task = task_fixture()
|
||||
assert Accounts.list_tasks() == [task]
|
||||
end
|
||||
|
||||
test "get_task!/1 returns the task with given id" do
|
||||
task = task_fixture()
|
||||
assert Accounts.get_task!(task.id) == task
|
||||
end
|
||||
|
||||
test "create_task/1 with valid data creates a task" do
|
||||
valid_attrs = %{description: "some description", phase: "some phase", emoji: "some emoji", completed_at: ~N[2024-01-21 05:40:00]}
|
||||
|
||||
assert {:ok, %Task{} = task} = Accounts.create_task(valid_attrs)
|
||||
assert task.description == "some description"
|
||||
assert task.phase == "some phase"
|
||||
assert task.emoji == "some emoji"
|
||||
assert task.completed_at == ~N[2024-01-21 05:40:00]
|
||||
end
|
||||
|
||||
test "create_task/1 with invalid data returns error changeset" do
|
||||
assert {:error, %Ecto.Changeset{}} = Accounts.create_task(@invalid_attrs)
|
||||
end
|
||||
|
||||
test "update_task/2 with valid data updates the task" do
|
||||
task = task_fixture()
|
||||
update_attrs = %{description: "some updated description", phase: "some updated phase", emoji: "some updated emoji", completed_at: ~N[2024-01-22 05:40:00]}
|
||||
|
||||
assert {:ok, %Task{} = task} = Accounts.update_task(task, update_attrs)
|
||||
assert task.description == "some updated description"
|
||||
assert task.phase == "some updated phase"
|
||||
assert task.emoji == "some updated emoji"
|
||||
assert task.completed_at == ~N[2024-01-22 05:40:00]
|
||||
end
|
||||
|
||||
test "update_task/2 with invalid data returns error changeset" do
|
||||
task = task_fixture()
|
||||
assert {:error, %Ecto.Changeset{}} = Accounts.update_task(task, @invalid_attrs)
|
||||
assert task == Accounts.get_task!(task.id)
|
||||
end
|
||||
|
||||
test "delete_task/1 deletes the task" do
|
||||
task = task_fixture()
|
||||
assert {:ok, %Task{}} = Accounts.delete_task(task)
|
||||
assert_raise Ecto.NoResultsError, fn -> Accounts.get_task!(task.id) end
|
||||
end
|
||||
|
||||
test "change_task/1 returns a task changeset" do
|
||||
task = task_fixture()
|
||||
assert %Ecto.Changeset{} = Accounts.change_task(task)
|
||||
end
|
||||
end
|
||||
end
|
113
test/homeman_web/live/task_live_test.exs
Normal file
113
test/homeman_web/live/task_live_test.exs
Normal file
|
@ -0,0 +1,113 @@
|
|||
defmodule HomemanWeb.TaskLiveTest do
|
||||
use HomemanWeb.ConnCase
|
||||
|
||||
import Phoenix.LiveViewTest
|
||||
import Homeman.AccountsFixtures
|
||||
|
||||
@create_attrs %{description: "some description", phase: "some phase", emoji: "some emoji", completed_at: "2024-01-21T05:40:00"}
|
||||
@update_attrs %{description: "some updated description", phase: "some updated phase", emoji: "some updated emoji", completed_at: "2024-01-22T05:40:00"}
|
||||
@invalid_attrs %{description: nil, phase: nil, emoji: nil, completed_at: nil}
|
||||
|
||||
defp create_task(_) do
|
||||
task = task_fixture()
|
||||
%{task: task}
|
||||
end
|
||||
|
||||
describe "Index" do
|
||||
setup [:create_task]
|
||||
|
||||
test "lists all tasks", %{conn: conn, task: task} do
|
||||
{:ok, _index_live, html} = live(conn, ~p"/tasks")
|
||||
|
||||
assert html =~ "Listing Tasks"
|
||||
assert html =~ task.description
|
||||
end
|
||||
|
||||
test "saves new task", %{conn: conn} do
|
||||
{:ok, index_live, _html} = live(conn, ~p"/tasks")
|
||||
|
||||
assert index_live |> element("a", "New Task") |> render_click() =~
|
||||
"New Task"
|
||||
|
||||
assert_patch(index_live, ~p"/tasks/new")
|
||||
|
||||
assert index_live
|
||||
|> form("#task-form", task: @invalid_attrs)
|
||||
|> render_change() =~ "can't be blank"
|
||||
|
||||
assert index_live
|
||||
|> form("#task-form", task: @create_attrs)
|
||||
|> render_submit()
|
||||
|
||||
assert_patch(index_live, ~p"/tasks")
|
||||
|
||||
html = render(index_live)
|
||||
assert html =~ "Task created successfully"
|
||||
assert html =~ "some description"
|
||||
end
|
||||
|
||||
test "updates task in listing", %{conn: conn, task: task} do
|
||||
{:ok, index_live, _html} = live(conn, ~p"/tasks")
|
||||
|
||||
assert index_live |> element("#tasks-#{task.id} a", "Edit") |> render_click() =~
|
||||
"Edit Task"
|
||||
|
||||
assert_patch(index_live, ~p"/tasks/#{task}/edit")
|
||||
|
||||
assert index_live
|
||||
|> form("#task-form", task: @invalid_attrs)
|
||||
|> render_change() =~ "can't be blank"
|
||||
|
||||
assert index_live
|
||||
|> form("#task-form", task: @update_attrs)
|
||||
|> render_submit()
|
||||
|
||||
assert_patch(index_live, ~p"/tasks")
|
||||
|
||||
html = render(index_live)
|
||||
assert html =~ "Task updated successfully"
|
||||
assert html =~ "some updated description"
|
||||
end
|
||||
|
||||
test "deletes task in listing", %{conn: conn, task: task} do
|
||||
{:ok, index_live, _html} = live(conn, ~p"/tasks")
|
||||
|
||||
assert index_live |> element("#tasks-#{task.id} a", "Delete") |> render_click()
|
||||
refute has_element?(index_live, "#tasks-#{task.id}")
|
||||
end
|
||||
end
|
||||
|
||||
describe "Show" do
|
||||
setup [:create_task]
|
||||
|
||||
test "displays task", %{conn: conn, task: task} do
|
||||
{:ok, _show_live, html} = live(conn, ~p"/tasks/#{task}")
|
||||
|
||||
assert html =~ "Show Task"
|
||||
assert html =~ task.description
|
||||
end
|
||||
|
||||
test "updates task within modal", %{conn: conn, task: task} do
|
||||
{:ok, show_live, _html} = live(conn, ~p"/tasks/#{task}")
|
||||
|
||||
assert show_live |> element("a", "Edit") |> render_click() =~
|
||||
"Edit Task"
|
||||
|
||||
assert_patch(show_live, ~p"/tasks/#{task}/show/edit")
|
||||
|
||||
assert show_live
|
||||
|> form("#task-form", task: @invalid_attrs)
|
||||
|> render_change() =~ "can't be blank"
|
||||
|
||||
assert show_live
|
||||
|> form("#task-form", task: @update_attrs)
|
||||
|> render_submit()
|
||||
|
||||
assert_patch(show_live, ~p"/tasks/#{task}")
|
||||
|
||||
html = render(show_live)
|
||||
assert html =~ "Task updated successfully"
|
||||
assert html =~ "some updated description"
|
||||
end
|
||||
end
|
||||
end
|
113
test/homeman_web/live/todo_live_test.exs
Normal file
113
test/homeman_web/live/todo_live_test.exs
Normal file
|
@ -0,0 +1,113 @@
|
|||
defmodule HomemanWeb.TodoLiveTest do
|
||||
use HomemanWeb.ConnCase
|
||||
|
||||
import Phoenix.LiveViewTest
|
||||
import Homeman.AccountsFixtures
|
||||
|
||||
@create_attrs %{description: "some description", emoji: "some emoji", completed_at: "2024-01-21T05:40:00"}
|
||||
@update_attrs %{description: "some updated description", emoji: "some updated emoji", completed_at: "2024-01-22T05:40:00"}
|
||||
@invalid_attrs %{description: nil, emoji: nil, completed_at: nil}
|
||||
|
||||
defp create_todo(_) do
|
||||
todo = todo_fixture()
|
||||
%{todo: todo}
|
||||
end
|
||||
|
||||
describe "Index" do
|
||||
setup [:create_todo]
|
||||
|
||||
test "lists all todos", %{conn: conn, todo: todo} do
|
||||
{:ok, _index_live, html} = live(conn, ~p"/todos")
|
||||
|
||||
assert html =~ "Listing Todos"
|
||||
assert html =~ todo.description
|
||||
end
|
||||
|
||||
test "saves new todo", %{conn: conn} do
|
||||
{:ok, index_live, _html} = live(conn, ~p"/todos")
|
||||
|
||||
assert index_live |> element("a", "New Todo") |> render_click() =~
|
||||
"New Todo"
|
||||
|
||||
assert_patch(index_live, ~p"/todos/new")
|
||||
|
||||
assert index_live
|
||||
|> form("#todo-form", todo: @invalid_attrs)
|
||||
|> render_change() =~ "can't be blank"
|
||||
|
||||
assert index_live
|
||||
|> form("#todo-form", todo: @create_attrs)
|
||||
|> render_submit()
|
||||
|
||||
assert_patch(index_live, ~p"/todos")
|
||||
|
||||
html = render(index_live)
|
||||
assert html =~ "Todo created successfully"
|
||||
assert html =~ "some description"
|
||||
end
|
||||
|
||||
test "updates todo in listing", %{conn: conn, todo: todo} do
|
||||
{:ok, index_live, _html} = live(conn, ~p"/todos")
|
||||
|
||||
assert index_live |> element("#todos-#{todo.id} a", "Edit") |> render_click() =~
|
||||
"Edit Todo"
|
||||
|
||||
assert_patch(index_live, ~p"/todos/#{todo}/edit")
|
||||
|
||||
assert index_live
|
||||
|> form("#todo-form", todo: @invalid_attrs)
|
||||
|> render_change() =~ "can't be blank"
|
||||
|
||||
assert index_live
|
||||
|> form("#todo-form", todo: @update_attrs)
|
||||
|> render_submit()
|
||||
|
||||
assert_patch(index_live, ~p"/todos")
|
||||
|
||||
html = render(index_live)
|
||||
assert html =~ "Todo updated successfully"
|
||||
assert html =~ "some updated description"
|
||||
end
|
||||
|
||||
test "deletes todo in listing", %{conn: conn, todo: todo} do
|
||||
{:ok, index_live, _html} = live(conn, ~p"/todos")
|
||||
|
||||
assert index_live |> element("#todos-#{todo.id} a", "Delete") |> render_click()
|
||||
refute has_element?(index_live, "#todos-#{todo.id}")
|
||||
end
|
||||
end
|
||||
|
||||
describe "Show" do
|
||||
setup [:create_todo]
|
||||
|
||||
test "displays todo", %{conn: conn, todo: todo} do
|
||||
{:ok, _show_live, html} = live(conn, ~p"/todos/#{todo}")
|
||||
|
||||
assert html =~ "Show Todo"
|
||||
assert html =~ todo.description
|
||||
end
|
||||
|
||||
test "updates todo within modal", %{conn: conn, todo: todo} do
|
||||
{:ok, show_live, _html} = live(conn, ~p"/todos/#{todo}")
|
||||
|
||||
assert show_live |> element("a", "Edit") |> render_click() =~
|
||||
"Edit Todo"
|
||||
|
||||
assert_patch(show_live, ~p"/todos/#{todo}/show/edit")
|
||||
|
||||
assert show_live
|
||||
|> form("#todo-form", todo: @invalid_attrs)
|
||||
|> render_change() =~ "can't be blank"
|
||||
|
||||
assert show_live
|
||||
|> form("#todo-form", todo: @update_attrs)
|
||||
|> render_submit()
|
||||
|
||||
assert_patch(show_live, ~p"/todos/#{todo}")
|
||||
|
||||
html = render(show_live)
|
||||
assert html =~ "Todo updated successfully"
|
||||
assert html =~ "some updated description"
|
||||
end
|
||||
end
|
||||
end
|
113
test/homeman_web/live/user_live_test.exs
Normal file
113
test/homeman_web/live/user_live_test.exs
Normal file
|
@ -0,0 +1,113 @@
|
|||
defmodule HomemanWeb.UserLiveTest do
|
||||
use HomemanWeb.ConnCase
|
||||
|
||||
import Phoenix.LiveViewTest
|
||||
import Homeman.AccountsFixtures
|
||||
|
||||
@create_attrs %{name: "some name", color: "some color", avatar_url: "some avatar_url"}
|
||||
@update_attrs %{name: "some updated name", color: "some updated color", avatar_url: "some updated avatar_url"}
|
||||
@invalid_attrs %{name: nil, color: nil, avatar_url: nil}
|
||||
|
||||
defp create_user(_) do
|
||||
user = user_fixture()
|
||||
%{user: user}
|
||||
end
|
||||
|
||||
describe "Index" do
|
||||
setup [:create_user]
|
||||
|
||||
test "lists all users", %{conn: conn, user: user} do
|
||||
{:ok, _index_live, html} = live(conn, ~p"/users")
|
||||
|
||||
assert html =~ "Listing Users"
|
||||
assert html =~ user.name
|
||||
end
|
||||
|
||||
test "saves new user", %{conn: conn} do
|
||||
{:ok, index_live, _html} = live(conn, ~p"/users")
|
||||
|
||||
assert index_live |> element("a", "New User") |> render_click() =~
|
||||
"New User"
|
||||
|
||||
assert_patch(index_live, ~p"/users/new")
|
||||
|
||||
assert index_live
|
||||
|> form("#user-form", user: @invalid_attrs)
|
||||
|> render_change() =~ "can't be blank"
|
||||
|
||||
assert index_live
|
||||
|> form("#user-form", user: @create_attrs)
|
||||
|> render_submit()
|
||||
|
||||
assert_patch(index_live, ~p"/users")
|
||||
|
||||
html = render(index_live)
|
||||
assert html =~ "User created successfully"
|
||||
assert html =~ "some name"
|
||||
end
|
||||
|
||||
test "updates user in listing", %{conn: conn, user: user} do
|
||||
{:ok, index_live, _html} = live(conn, ~p"/users")
|
||||
|
||||
assert index_live |> element("#users-#{user.id} a", "Edit") |> render_click() =~
|
||||
"Edit User"
|
||||
|
||||
assert_patch(index_live, ~p"/users/#{user}/edit")
|
||||
|
||||
assert index_live
|
||||
|> form("#user-form", user: @invalid_attrs)
|
||||
|> render_change() =~ "can't be blank"
|
||||
|
||||
assert index_live
|
||||
|> form("#user-form", user: @update_attrs)
|
||||
|> render_submit()
|
||||
|
||||
assert_patch(index_live, ~p"/users")
|
||||
|
||||
html = render(index_live)
|
||||
assert html =~ "User updated successfully"
|
||||
assert html =~ "some updated name"
|
||||
end
|
||||
|
||||
test "deletes user in listing", %{conn: conn, user: user} do
|
||||
{:ok, index_live, _html} = live(conn, ~p"/users")
|
||||
|
||||
assert index_live |> element("#users-#{user.id} a", "Delete") |> render_click()
|
||||
refute has_element?(index_live, "#users-#{user.id}")
|
||||
end
|
||||
end
|
||||
|
||||
describe "Show" do
|
||||
setup [:create_user]
|
||||
|
||||
test "displays user", %{conn: conn, user: user} do
|
||||
{:ok, _show_live, html} = live(conn, ~p"/users/#{user}")
|
||||
|
||||
assert html =~ "Show User"
|
||||
assert html =~ user.name
|
||||
end
|
||||
|
||||
test "updates user within modal", %{conn: conn, user: user} do
|
||||
{:ok, show_live, _html} = live(conn, ~p"/users/#{user}")
|
||||
|
||||
assert show_live |> element("a", "Edit") |> render_click() =~
|
||||
"Edit User"
|
||||
|
||||
assert_patch(show_live, ~p"/users/#{user}/show/edit")
|
||||
|
||||
assert show_live
|
||||
|> form("#user-form", user: @invalid_attrs)
|
||||
|> render_change() =~ "can't be blank"
|
||||
|
||||
assert show_live
|
||||
|> form("#user-form", user: @update_attrs)
|
||||
|> render_submit()
|
||||
|
||||
assert_patch(show_live, ~p"/users/#{user}")
|
||||
|
||||
html = render(show_live)
|
||||
assert html =~ "User updated successfully"
|
||||
assert html =~ "some updated name"
|
||||
end
|
||||
end
|
||||
end
|
55
test/support/fixtures/accounts_fixtures.ex
Normal file
55
test/support/fixtures/accounts_fixtures.ex
Normal file
|
@ -0,0 +1,55 @@
|
|||
defmodule Homeman.AccountsFixtures do
|
||||
@moduledoc """
|
||||
This module defines test helpers for creating
|
||||
entities via the `Homeman.Accounts` context.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Generate a user.
|
||||
"""
|
||||
def user_fixture(attrs \\ %{}) do
|
||||
{:ok, user} =
|
||||
attrs
|
||||
|> Enum.into(%{
|
||||
avatar_url: "some avatar_url",
|
||||
color: "some color",
|
||||
name: "some name"
|
||||
})
|
||||
|> Homeman.Accounts.create_user()
|
||||
|
||||
user
|
||||
end
|
||||
|
||||
@doc """
|
||||
Generate a todo.
|
||||
"""
|
||||
def todo_fixture(attrs \\ %{}) do
|
||||
{:ok, todo} =
|
||||
attrs
|
||||
|> Enum.into(%{
|
||||
completed_at: ~N[2024-01-21 05:40:00],
|
||||
description: "some description",
|
||||
emoji: "some emoji"
|
||||
})
|
||||
|> Homeman.Accounts.create_todo()
|
||||
|
||||
todo
|
||||
end
|
||||
|
||||
@doc """
|
||||
Generate a task.
|
||||
"""
|
||||
def task_fixture(attrs \\ %{}) do
|
||||
{:ok, task} =
|
||||
attrs
|
||||
|> Enum.into(%{
|
||||
completed_at: ~N[2024-01-21 05:40:00],
|
||||
description: "some description",
|
||||
emoji: "some emoji",
|
||||
phase: "some phase"
|
||||
})
|
||||
|> Homeman.Accounts.create_task()
|
||||
|
||||
task
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue