diff --git a/.gitignore b/.gitignore
index 899f4fa..62755bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,3 +36,6 @@ npm-debug.log
/assets/node_modules/
.env
envs/
+
+# VSCode configuration
+/.vscode/
diff --git a/README.md b/README.md
index a2d3a82..bfeb501 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,36 @@
-# cklist
+# ✔️ cklist
-Awesome checklists are about to come here. Stay tuned.
+Awesome checklists are about to come here. Stay tuned...
-## Contributing
+## 🤝 Contributing
-Contributions are welcome.
+We are just getting started. Feel free to reach out if you feel like joining.
-- cklists are based on the awesome [Phoenix Framework](https://www.phoenixframework.org/).
-- Elixir & Erlang versions are managed with [asdf](https://asdf-vm.com/) in [.tool-versions](.tool-versions).
-- We use [PostgreSQL](https://www.postgresql.org/) as database backend. For local development, we assume a user `cklist` exists (see [config/dev.exs](./config/dev.exs)). The authentication system makes use of the `citext` extension of PostgreSQL. If DB migration complains about missing the `citext` extension, try search for and installing the `postgres-contrib` package.
+### Setup notes
+
+#### Framework
+
+cklists are based on the awesome [Phoenix Framework](https://www.phoenixframework.org/).
+
+#### Versions
+
+Elixir, Erlang, and npm versions are managed with [asdf](https://asdf-vm.com/) in [.tool-versions](.tool-versions).
+
+#### Database
+
+We use [PostgreSQL](https://www.postgresql.org/) as database backend.
+
+
+Database details
+
+Phoenix' authentication system makes use of the `citext` extension of PostgreSQL. If DB migration complains about missing the `citext` extension, try search for and installing the `postgres-contrib` package.
+
+Database secrets can be stored in `.env`-style configuration. This is useful for both, local development and deployments. To override the database configuration, put the following contents
+
+```none
+DATABASE_URL="ecto://cklist:cklist@localhost/cklist_dev"
+TEST_DATABASE_URL="ecto://cklist:cklist@localhost/cklist_test"
+```
+
+in a file `envs/.env` and adapt the defaults to your liking.
+
diff --git a/lib/cklist/accounts/user.ex b/lib/cklist/accounts/user.ex
index 9447257..0c42bb7 100644
--- a/lib/cklist/accounts/user.ex
+++ b/lib/cklist/accounts/user.ex
@@ -8,6 +8,8 @@ defmodule Cklist.Accounts.User do
field :hashed_password, :string, redact: true
field :confirmed_at, :naive_datetime
+ has_many :checklists, Cklist.Checklists.Checklist
+
timestamps(type: :utc_datetime)
end
diff --git a/lib/cklist/checklists.ex b/lib/cklist/checklists.ex
new file mode 100644
index 0000000..1fb159e
--- /dev/null
+++ b/lib/cklist/checklists.ex
@@ -0,0 +1,149 @@
+defmodule Cklist.Checklists do
+ @moduledoc """
+ The Checklists context.
+ """
+
+ import Ecto.Query, warn: false
+ alias Cklist.Checklists.Run
+ alias Cklist.Checklists.Activity
+ alias Cklist.Checklists.Checklist
+ alias Cklist.Repo
+
+ @doc """
+ Returns the list of checklists.
+
+ ## Examples
+
+ iex> list_checklists()
+ [%Checklist{}, ...]
+
+ """
+ def list_checklists(nil) do
+ query = from l in Checklist, where: l.access == :public
+ Repo.all(query)
+ end
+ def list_checklists(user) do
+ query = from l in Checklist, where: l.user_id == ^user.id or l.access == :public
+ Repo.all(query)
+ end
+
+
+ @doc """
+ Gets a single checklist.
+
+ Raises `Ecto.NoResultsError` if the Checklist does not exist.
+
+ ## Examples
+
+ iex> get_checklist!(123)
+ %Checklist{}
+
+ iex> get_checklist!(456)
+ ** (Ecto.NoResultsError)
+
+ """
+ def get_checklist!(id), do: Repo.get!(Checklist, id)
+
+ @doc """
+ Creates a checklist.
+
+ ## Examples
+
+ iex> create_checklist(%{field: value})
+ {:ok, %Checklist{}}
+
+ iex> create_checklist(%{field: bad_value})
+ {:error, %Ecto.Changeset{}}
+
+ """
+ def create_checklist(attrs \\ %{}) do
+ %Checklist{}
+ |> Checklist.changeset(attrs)
+ |> Repo.insert()
+ end
+
+ @doc """
+ Inserts a checklist
+ """
+ def insert_checklist(changeset) do
+ Repo.insert(changeset)
+ end
+
+ @doc """
+ Updates a checklist.
+
+ ## Examples
+
+ iex> update_checklist(checklist, %{field: new_value})
+ {:ok, %Checklist{}}
+
+ iex> update_checklist(checklist, %{field: bad_value})
+ {:error, %Ecto.Changeset{}}
+
+ """
+ def update_checklist(%Checklist{} = checklist, attrs) do
+ checklist
+ |> Checklist.changeset(attrs)
+ |> Repo.update()
+ end
+
+ @doc """
+ Deletes a checklist.
+
+ ## Examples
+
+ iex> delete_checklist(checklist)
+ {:ok, %Checklist{}}
+
+ iex> delete_checklist(checklist)
+ {:error, %Ecto.Changeset{}}
+
+ """
+ def delete_checklist(%Checklist{} = checklist) do
+ Repo.delete(checklist)
+ end
+
+ @doc """
+ Returns an `%Ecto.Changeset{}` for tracking checklist changes.
+
+ ## Examples
+
+ iex> change_checklist(checklist)
+ %Ecto.Changeset{data: %Checklist{}}
+
+ """
+ def change_checklist(%Checklist{} = checklist, attrs \\ %{}) do
+ Checklist.changeset(checklist, attrs)
+ end
+
+ def log_run_start(checklist, user) do
+ {:ok, run} = %Run{}
+ |> Run.changeset(%{checklist_id: checklist.id})
+ |> Repo.insert()
+
+ %Activity{}
+ |> Activity.changeset(%{run_id: run.id, user_id: user.id, event: %{type: "checklist_start"}})
+ |> Repo.insert()
+
+ # return current run
+ run
+ end
+
+ def log_run_abort(run, user) do
+ %Activity{}
+ |> Activity.changeset(%{run_id: run.id, user_id: user.id, event: %{type: "checklist_abort"}})
+ |> Repo.insert()
+ end
+
+ def log_run_complete(run, user) do
+ %Activity{}
+ |> Activity.changeset(%{run_id: run.id, user_id: user.id, event: %{type: "checklist_complete"}})
+ |> Repo.insert()
+ end
+
+ def log_step_complete(run, user, step_id, is_done \\ true) do
+ %Activity{}
+ |> Activity.changeset(%{run_id: run.id, user_id: user.id, event: %{type: "step_done", step_id: step_id, done: is_done}})
+ |> Repo.insert()
+ end
+end
diff --git a/lib/cklist/checklists/activity.ex b/lib/cklist/checklists/activity.ex
new file mode 100644
index 0000000..2923100
--- /dev/null
+++ b/lib/cklist/checklists/activity.ex
@@ -0,0 +1,22 @@
+defmodule Cklist.Checklists.Activity do
+ use Ecto.Schema
+ import Ecto.Changeset
+
+ schema "activity" do
+ # We assume events have a type. Any other properties are optional and type-dependent.
+ field :event, :map
+
+ belongs_to :run, Cklist.Checklists.Run
+ belongs_to :user, Cklist.Accounts.User
+
+ # We don't expect activities to be modified.
+ timestamps([type: :utc_datetime, updated_at: false])
+ end
+
+ @doc false
+ def changeset(activity, attrs) do
+ activity
+ |> cast(attrs, [:event, :run_id, :user_id])
+ |> validate_required([:event, :run_id, :user_id])
+ end
+end
diff --git a/lib/cklist/checklists/checklist.ex b/lib/cklist/checklists/checklist.ex
new file mode 100644
index 0000000..30108e7
--- /dev/null
+++ b/lib/cklist/checklists/checklist.ex
@@ -0,0 +1,23 @@
+defmodule Cklist.Checklists.Checklist do
+ use Ecto.Schema
+ import Ecto.Changeset
+
+ schema "checklists" do
+ field :title, :string
+ field :description, :string
+ field :access, Ecto.Enum, values: [:public, :personal]
+ field :document, :map
+
+ timestamps(type: :utc_datetime)
+
+ belongs_to :user, Cklist.Accounts.User
+ has_many :runs, Cklist.Checklists.Run, on_delete: :delete_all
+ end
+
+ @doc false
+ def changeset(checklist, attrs) do
+ checklist
+ |> cast(attrs, [:title, :description, :access, :document, :user_id])
+ |> validate_required([:title, :description, :access, :document, :user_id])
+ end
+end
diff --git a/lib/cklist/checklists/run.ex b/lib/cklist/checklists/run.ex
new file mode 100644
index 0000000..b5f4651
--- /dev/null
+++ b/lib/cklist/checklists/run.ex
@@ -0,0 +1,16 @@
+defmodule Cklist.Checklists.Run do
+ use Ecto.Schema
+ import Ecto.Changeset
+
+ schema "runs" do
+ belongs_to :checklist, Cklist.Checklists.Checklist
+ has_many :activities, Cklist.Checklists.Activity, on_delete: :delete_all
+ end
+
+ @doc false
+ def changeset(run, attrs) do
+ run
+ |> cast(attrs, [:checklist_id])
+ |> validate_required([:checklist_id])
+ end
+end
diff --git a/lib/cklist_web.ex b/lib/cklist_web.ex
index 1c5c75b..3351dce 100644
--- a/lib/cklist_web.ex
+++ b/lib/cklist_web.ex
@@ -85,6 +85,7 @@ defmodule CklistWeb do
import Phoenix.HTML
# Core UI components and translation
import CklistWeb.CoreComponents
+ import CklistWeb.MyComponents
import Cklist.Gettext
# Shortcut for generating JS commands
diff --git a/lib/cklist_web/components/core_components.ex b/lib/cklist_web/components/core_components.ex
index eed9736..f6d94cb 100644
--- a/lib/cklist_web/components/core_components.ex
+++ b/lib/cklist_web/components/core_components.ex
@@ -501,7 +501,7 @@ defmodule CklistWeb.CoreComponents do
-
+ |
-
+
-
- v<%= Application.spec(:phoenix, :vsn) %>
+
+ cklist
diff --git a/lib/cklist_web/components/layouts/root.html.heex b/lib/cklist_web/components/layouts/root.html.heex
index 8e2ac1d..5837573 100644
--- a/lib/cklist_web/components/layouts/root.html.heex
+++ b/lib/cklist_web/components/layouts/root.html.heex
@@ -12,47 +12,6 @@
-
- <%= if @current_users do %>
- -
- <%= @current_users.email %>
-
- -
- <.link
- href={~p"/users/settings"}
- class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700"
- >
- Settings
-
-
- -
- <.link
- href={~p"/users/log_out"}
- method="delete"
- class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700"
- >
- Log out
-
-
- <% else %>
- -
- <.link
- href={~p"/users/register"}
- class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700"
- >
- Register
-
-
- -
- <.link
- href={~p"/users/log_in"}
- class="text-[0.8125rem] leading-6 text-zinc-900 font-semibold hover:text-zinc-700"
- >
- Log in
-
-
- <% end %>
-
<%= @inner_content %>
|