Skip to content
Draft

MVP #11

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
84d6b00
Update Readme
Jan 28, 2024
818f34a
Ignore VSCode configuration folder
Jan 28, 2024
f528a41
Basic checklist ressource
Jan 28, 2024
6137075
Only :index of ChecklistController is public
Jan 28, 2024
d921abf
Basic HTML rebranding
Jan 28, 2024
b686492
Users -> user
aseigo Jan 29, 2024
ef741b5
email is no longer shown on the page when authed
aseigo Jan 29, 2024
e1b2f0a
fix test
aseigo Jan 29, 2024
534c99a
authenticate for these tests
aseigo Jan 29, 2024
9c39f1c
maps need to be serialized
aseigo Jan 29, 2024
85744b3
Save user_id when creating a new checklist
Feb 5, 2024
5a9741e
WIP: also save some standard document
Feb 5, 2024
51973eb
Show all properties of the checklist
Feb 5, 2024
cc80fe9
WIP: Add buttons to run / edit / delete checklists
Feb 5, 2024
6a2a20b
Add access property on checklist
Feb 11, 2024
6bbaae2
WIP: Run simple, non-sequential cheklists
Feb 18, 2024
d18a1d0
Track state of checked tasks
Feb 25, 2024
8948f5c
Completed checklists don't change anymore
Feb 25, 2024
a47a568
Generated checklist are now sequential
Feb 25, 2024
12d3a72
Simple UI for sequential chcklists
Feb 25, 2024
7201568
Same look & feel as for sequential checklists
Feb 25, 2024
a8038f6
Move buttons to re-useable components
Mar 3, 2024
3a835e0
Use seeds file to pre-populate dev database
Mar 4, 2024
5dcdbab
Log to database (#24)
romanc Mar 10, 2024
16f31bd
WIP: UI to create checklists
Mar 17, 2024
a31dbc7
Move new cklist to live view
Mar 23, 2024
6bc8fad
WIP live updating checklist steps
Mar 24, 2024
93368fc
WIP: half-working live view for saving new checklists
Apr 7, 2024
79da2f5
WIP fix issue with undefined enum values
Apr 15, 2024
273ae1b
WIP: First working version of checklist generation
Apr 15, 2024
475be3a
Fix checklist creation and cleanup
May 2, 2024
f761e2a
Fix on_delete behavior
May 2, 2024
240c116
Disallow editing checklists
May 2, 2024
a0efd15
use heroicons
May 6, 2024
b474fab
Restrict what non-users can see
May 6, 2024
d4da980
Work on cards for sequential checklists
May 6, 2024
2402887
Tweakig buttons
May 6, 2024
d8a21dc
At least put a flash note if creation failed
May 12, 2024
59715a0
More components and other random cleanups
May 12, 2024
3cad4ee
README cleanups
May 12, 2024
00aa2d5
fix line ending
Jul 13, 2024
68ee71d
Add support for step descriptions
Jul 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ npm-debug.log
/assets/node_modules/
.env
envs/

# VSCode configuration
/.vscode/
39 changes: 32 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -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.

<details>
<summary>Database details</summary>

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.
</details>
2 changes: 2 additions & 0 deletions lib/cklist/accounts/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
149 changes: 149 additions & 0 deletions lib/cklist/checklists.ex
Original file line number Diff line number Diff line change
@@ -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
22 changes: 22 additions & 0 deletions lib/cklist/checklists/activity.ex
Original file line number Diff line number Diff line change
@@ -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
23 changes: 23 additions & 0 deletions lib/cklist/checklists/checklist.ex
Original file line number Diff line number Diff line change
@@ -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
16 changes: 16 additions & 0 deletions lib/cklist/checklists/run.ex
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions lib/cklist_web.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/cklist_web/components/core_components.ex
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ defmodule CklistWeb.CoreComponents do
</span>
</div>
</td>
<td :if={@action != []} class="relative w-14 p-0">
<td :if={@action != []} class="relative p-0">
<div class="relative whitespace-nowrap py-4 text-right text-sm font-medium">
<span class="absolute -inset-y-px -right-4 left-0 group-hover:bg-zinc-50 sm:rounded-r-xl" />
<span
Expand Down
51 changes: 38 additions & 13 deletions lib/cklist_web/components/layouts/app.html.heex
Original file line number Diff line number Diff line change
@@ -1,26 +1,51 @@
<header class="px-4 sm:px-6 lg:px-8">
<div class="flex items-center justify-between border-b border-zinc-100 py-3 text-sm">
<div class="flex items-center gap-4">
<a href="/">
<a href={~p"/"}>
<img src={~p"/images/logo.svg"} width="36" />
</a>
<p class="bg-brand/5 text-brand rounded-full px-2 font-medium leading-6">
v<%= Application.spec(:phoenix, :vsn) %>
<p class="text-[1.5rem]">
<a href={~p"/"} >cklist</a>
</p>
</div>
<div class="flex items-center gap-4 font-semibold leading-6 text-zinc-900">
<a href="https://twitter.com/elixirphoenix" class="hover:text-zinc-700">
@elixirphoenix
</a>
<a href="https://github.com/phoenixframework/phoenix" class="hover:text-zinc-700">
<%= if @current_user do %>
<p>
<%= @current_user.email %>
</p>
<.link
href={~p"/user/settings"}
class="hover:text-zinc-700"
>
Settings
</.link>
<.link
href={~p"/user/log_out"}
method="delete"
class="hover:text-zinc-700"
>
Log out
</.link>
<% else %>
<.link
href={~p"/user/register"}
class="hover:text-zinc-700"
>
Register
</.link>
<.link
href={~p"/user/log_in"}
class="hover:text-zinc-700"
>
Log in
</.link>
<% end %>

<!--
<a href="https://github.com/romanc/cklist" class="hover:text-zinc-700">
GitHub
</a>
<a
href="https://hexdocs.pm/phoenix/overview.html"
class="rounded-lg bg-zinc-100 px-2 py-1 hover:bg-zinc-200/80"
>
Get Started <span aria-hidden="true">&rarr;</span>
</a>
-->
</div>
</div>
</header>
Expand Down
Loading