This commit is contained in:
Ondřej 2024-08-09 16:11:31 +02:00
parent c9791acff8
commit 8b17c987d8
4 changed files with 57 additions and 41 deletions

View file

@ -1,6 +1,7 @@
defmodule ChoreTracker.Accounts.User do
use Ecto.Schema
import Ecto.Changeset
alias ChoreTracker.Chores
schema "users" do
field :username, :string
@ -12,6 +13,8 @@ defmodule ChoreTracker.Accounts.User do
field :display_name, :string
timestamps(type: :utc_datetime)
has_many :assigned_chores, Chores.Chore, foreign_key: :next_assignee_id
end
@doc """

View file

@ -19,24 +19,41 @@ defmodule ChoreTracker.Chores do
do: Repo.get!(Chore, id) |> Repo.preload([{:logs, :user}, :assignees, :next_assignee])
def create_chore(attrs \\ %{}) do
%Chore{}
|> change_chore(attrs)
|> Repo.insert()
result =
Ecto.Multi.new()
|> Ecto.Multi.insert(:chore, change_chore(%Chore{}, attrs))
|> Ecto.Multi.update(:chore_with_assignee, fn %{chore: chore} ->
next_assignee = next_chore_assignee(chore)
change_chore_assignee(chore, next_assignee)
end)
|> Repo.transaction()
case result do
{:ok, %{chore_with_assignee: chore}} -> {:ok, chore}
{:error, :chore, changeset, _} -> {:error, changeset}
end
end
def update_chore(%Chore{} = chore, attrs) do
changeset = change_chore(chore, attrs)
result =
Ecto.Multi.new()
|> Ecto.Multi.update(:chore, changeset)
|> Ecto.Multi.run(:update_next_assignee, fn _repo, %{chore: chore} ->
|> Ecto.Multi.update(:chore_with_assignee, fn %{chore: chore} ->
if Ecto.Changeset.changed?(changeset, :assignees) do
save_next_chore_assignee(chore)
next_assignee = next_chore_assignee(chore)
change_chore_assignee(chore, next_assignee)
else
{:ok, chore}
Ecto.Changeset.change(chore)
end
end)
|> Repo.transaction()
case result do
{:ok, %{chore_with_assignee: chore}} -> {:ok, chore}
{:error, :chore, changeset, _} -> {:error, changeset}
end
end
def delete_chore(%Chore{} = chore) do
@ -51,12 +68,14 @@ defmodule ChoreTracker.Chores do
|> Changeset.put_assoc(:assignees, assignees)
end
def update_chore_assignee(%Chore{} = chore, attrs) do
change_chore_assignee(chore, attrs) |> Repo.update()
def change_chore_assignee(%Chore{} = chore, user) do
id =
case user do
nil -> nil
%User{} -> user.id
end
def change_chore_assignee(%Chore{} = chore, attrs \\ %{}) do
chore |> Ecto.Changeset.cast(attrs, [:next_assignee_id])
chore |> Ecto.Changeset.change(%{next_assignee_id: id})
end
def get_last_chore_log_for_assignee(%Chore{} = chore, %User{} = user) do
@ -74,8 +93,9 @@ defmodule ChoreTracker.Chores do
:log,
%ChoreLog{} |> ChoreLog.changeset(%{user_id: user.id, chore_id: chore.id})
)
|> Ecto.Multi.run(:chore, fn _repo, _change ->
save_next_chore_assignee(chore)
|> Ecto.Multi.update(:chore_assignee, fn _ ->
next_assignee = next_chore_assignee(chore)
change_chore_assignee(chore, next_assignee)
end)
|> Repo.transaction()
end
@ -113,28 +133,21 @@ defmodule ChoreTracker.Chores do
Returns `nil` if chore has no assignees.
"""
def next_chore_assignee(%Chore{} = chore) do
chore = chore |> Repo.preload([{:assignees, :assigned_chores}])
chore.assignees
|> Enum.map(&{&1, get_last_chore_log_for_assignee(chore, &1)})
|> Enum.sort_by(
fn {_assignee, log} ->
case log do
fn assignee ->
time =
case get_last_chore_log_for_assignee(chore, assignee) do
nil -> 0
%ChoreLog{inserted_at: inserted_at} -> DateTime.to_unix(inserted_at)
end
{time, length(assignee.assigned_chores)}
end,
:asc
:desc
)
|> Enum.map(fn {assignee, _log} -> assignee end)
|> List.first()
end
defp save_next_chore_assignee(%Chore{} = chore) do
next_assignee = next_chore_assignee(chore)
if next_assignee do
chore |> update_chore_assignee(%{next_assignee_id: next_assignee.id})
else
{:ok, nil}
end
end
end

View file

@ -21,7 +21,7 @@ defmodule ChoreTrackerWeb.ChoreLive.FormComponent do
>
<div class="flex gap-2 flex-wrap">
<.input field={@form[:name]} type="text" label="Name" wrapper_class="flex-1" />
<.input field={@form[:emoji]} type="text" label="Emoji" />
<.input field={@form[:emoji]} type="text" label="Emoji" min="1" max="1" />
</div>
<.input field={@form[:description]} type="textarea" label="Description" />
<div class="flex gap-6 flex-wrap">

View file

@ -41,7 +41,7 @@ defmodule ChoreTrackerWeb.ChoreLive.Show do
%{"chore" => chore_params} = params
chore = socket.assigns.chore
case Chores.update_chore_assignee(chore, chore_params) do
case Chores.update_chore(chore, chore_params) do
{:ok, chore} ->
{:noreply,
socket
@ -57,6 +57,6 @@ defmodule ChoreTrackerWeb.ChoreLive.Show do
defp update_state(socket, chore) do
socket
|> assign(:chore, chore |> Repo.preload(:next_assignee))
|> assign(:assignee_form, to_form(Chores.change_chore_assignee(chore)))
|> assign(:assignee_form, to_form(Chores.change_chore(chore)))
end
end