From 21ee3298b63f966fa251966c22cc9971b1e4c5b6 Mon Sep 17 00:00:00 2001 From: Inhji Date: Sun, 5 Mar 2023 17:23:16 +0100 Subject: [PATCH] add notes --- lib/chiya/notes.ex | 104 ++++++++++++++++++ lib/chiya/notes/note.ex | 23 ++++ lib/chiya_web/controllers/note_controller.ex | 62 +++++++++++ lib/chiya_web/controllers/note_html.ex | 13 +++ .../controllers/note_html/edit.html.heex | 8 ++ .../controllers/note_html/index.html.heex | 28 +++++ .../controllers/note_html/new.html.heex | 8 ++ .../controllers/note_html/note_form.html.heex | 14 +++ .../controllers/note_html/show.html.heex | 20 ++++ lib/chiya_web/router.ex | 1 + .../20230305162242_create_notes.exs | 18 +++ test/chiya/notes_test.exs | 69 ++++++++++++ .../controllers/note_controller_test.exs | 84 ++++++++++++++ test/support/fixtures/notes_fixtures.ex | 30 +++++ 14 files changed, 482 insertions(+) create mode 100644 lib/chiya/notes.ex create mode 100644 lib/chiya/notes/note.ex create mode 100644 lib/chiya_web/controllers/note_controller.ex create mode 100644 lib/chiya_web/controllers/note_html.ex create mode 100644 lib/chiya_web/controllers/note_html/edit.html.heex create mode 100644 lib/chiya_web/controllers/note_html/index.html.heex create mode 100644 lib/chiya_web/controllers/note_html/new.html.heex create mode 100644 lib/chiya_web/controllers/note_html/note_form.html.heex create mode 100644 lib/chiya_web/controllers/note_html/show.html.heex create mode 100644 priv/repo/migrations/20230305162242_create_notes.exs create mode 100644 test/chiya/notes_test.exs create mode 100644 test/chiya_web/controllers/note_controller_test.exs create mode 100644 test/support/fixtures/notes_fixtures.ex diff --git a/lib/chiya/notes.ex b/lib/chiya/notes.ex new file mode 100644 index 0000000..4dfda98 --- /dev/null +++ b/lib/chiya/notes.ex @@ -0,0 +1,104 @@ +defmodule Chiya.Notes do + @moduledoc """ + The Notes context. + """ + + import Ecto.Query, warn: false + alias Chiya.Repo + + alias Chiya.Notes.Note + + @doc """ + Returns the list of notes. + + ## Examples + + iex> list_notes() + [%Note{}, ...] + + """ + def list_notes do + Repo.all(Note) + end + + @doc """ + Gets a single note. + + Raises `Ecto.NoResultsError` if the Note does not exist. + + ## Examples + + iex> get_note!(123) + %Note{} + + iex> get_note!(456) + ** (Ecto.NoResultsError) + + """ + def get_note!(id), do: Repo.get!(Note, id) + + @doc """ + Creates a note. + + ## Examples + + iex> create_note(%{field: value}) + {:ok, %Note{}} + + iex> create_note(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_note(attrs \\ %{}) do + %Note{} + |> Note.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a note. + + ## Examples + + iex> update_note(note, %{field: new_value}) + {:ok, %Note{}} + + iex> update_note(note, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_note(%Note{} = note, attrs) do + note + |> Note.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a note. + + ## Examples + + iex> delete_note(note) + {:ok, %Note{}} + + iex> delete_note(note) + {:error, %Ecto.Changeset{}} + + """ + def delete_note(%Note{} = note) do + Repo.delete(note) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking note changes. + + ## Examples + + iex> change_note(note) + %Ecto.Changeset{data: %Note{}} + + """ + def change_note(%Note{} = note, attrs \\ %{}) do + Note.changeset(note, attrs) + end +end diff --git a/lib/chiya/notes/note.ex b/lib/chiya/notes/note.ex new file mode 100644 index 0000000..94c5cef --- /dev/null +++ b/lib/chiya/notes/note.ex @@ -0,0 +1,23 @@ +defmodule Chiya.Notes.Note do + use Ecto.Schema + import Ecto.Changeset + + schema "notes" do + field :content, :string + field :kind, :string + field :name, :string + field :published_at, :naive_datetime + field :slug, :string + field :url, :string + + timestamps() + end + + @doc false + def changeset(note, attrs) do + note + |> cast(attrs, [:name, :content, :slug, :published_at, :kind, :url]) + |> validate_required([:name, :content, :slug, :published_at, :kind, :url]) + |> unique_constraint(:slug) + end +end diff --git a/lib/chiya_web/controllers/note_controller.ex b/lib/chiya_web/controllers/note_controller.ex new file mode 100644 index 0000000..321dd80 --- /dev/null +++ b/lib/chiya_web/controllers/note_controller.ex @@ -0,0 +1,62 @@ +defmodule ChiyaWeb.NoteController do + use ChiyaWeb, :controller + + alias Chiya.Notes + alias Chiya.Notes.Note + + def index(conn, _params) do + notes = Notes.list_notes() + render(conn, :index, notes: notes) + end + + def new(conn, _params) do + changeset = Notes.change_note(%Note{}) + render(conn, :new, changeset: changeset) + end + + def create(conn, %{"note" => note_params}) do + case Notes.create_note(note_params) do + {:ok, note} -> + conn + |> put_flash(:info, "Note created successfully.") + |> redirect(to: ~p"/notes/#{note}") + + {:error, %Ecto.Changeset{} = changeset} -> + render(conn, :new, changeset: changeset) + end + end + + def show(conn, %{"id" => id}) do + note = Notes.get_note!(id) + render(conn, :show, note: note) + end + + def edit(conn, %{"id" => id}) do + note = Notes.get_note!(id) + changeset = Notes.change_note(note) + render(conn, :edit, note: note, changeset: changeset) + end + + def update(conn, %{"id" => id, "note" => note_params}) do + note = Notes.get_note!(id) + + case Notes.update_note(note, note_params) do + {:ok, note} -> + conn + |> put_flash(:info, "Note updated successfully.") + |> redirect(to: ~p"/notes/#{note}") + + {:error, %Ecto.Changeset{} = changeset} -> + render(conn, :edit, note: note, changeset: changeset) + end + end + + def delete(conn, %{"id" => id}) do + note = Notes.get_note!(id) + {:ok, _note} = Notes.delete_note(note) + + conn + |> put_flash(:info, "Note deleted successfully.") + |> redirect(to: ~p"/notes") + end +end diff --git a/lib/chiya_web/controllers/note_html.ex b/lib/chiya_web/controllers/note_html.ex new file mode 100644 index 0000000..8fe86bc --- /dev/null +++ b/lib/chiya_web/controllers/note_html.ex @@ -0,0 +1,13 @@ +defmodule ChiyaWeb.NoteHTML do + use ChiyaWeb, :html + + embed_templates "note_html/*" + + @doc """ + Renders a note form. + """ + attr :changeset, Ecto.Changeset, required: true + attr :action, :string, required: true + + def note_form(assigns) +end diff --git a/lib/chiya_web/controllers/note_html/edit.html.heex b/lib/chiya_web/controllers/note_html/edit.html.heex new file mode 100644 index 0000000..0be44de --- /dev/null +++ b/lib/chiya_web/controllers/note_html/edit.html.heex @@ -0,0 +1,8 @@ +<.header> + Edit Note <%= @note.id %> + <:subtitle>Use this form to manage note records in your database. + + +<.note_form changeset={@changeset} action={~p"/notes/#{@note}"} /> + +<.back navigate={~p"/notes"}>Back to notes diff --git a/lib/chiya_web/controllers/note_html/index.html.heex b/lib/chiya_web/controllers/note_html/index.html.heex new file mode 100644 index 0000000..ca6281f --- /dev/null +++ b/lib/chiya_web/controllers/note_html/index.html.heex @@ -0,0 +1,28 @@ +<.header> + Listing Notes + <:actions> + <.link href={~p"/notes/new"}> + <.button>New Note + + + + +<.table id="notes" rows={@notes} row_click={&JS.navigate(~p"/notes/#{&1}")}> + <:col :let={note} label="Name"><%= note.name %> + <:col :let={note} label="Content"><%= note.content %> + <:col :let={note} label="Slug"><%= note.slug %> + <:col :let={note} label="Published at"><%= note.published_at %> + <:col :let={note} label="Kind"><%= note.kind %> + <:col :let={note} label="Url"><%= note.url %> + <:action :let={note}> +
+ <.link navigate={~p"/notes/#{note}"}>Show +
+ <.link navigate={~p"/notes/#{note}/edit"}>Edit + + <:action :let={note}> + <.link href={~p"/notes/#{note}"} method="delete" data-confirm="Are you sure?"> + Delete + + + diff --git a/lib/chiya_web/controllers/note_html/new.html.heex b/lib/chiya_web/controllers/note_html/new.html.heex new file mode 100644 index 0000000..4cf47a4 --- /dev/null +++ b/lib/chiya_web/controllers/note_html/new.html.heex @@ -0,0 +1,8 @@ +<.header> + New Note + <:subtitle>Use this form to manage note records in your database. + + +<.note_form changeset={@changeset} action={~p"/notes"} /> + +<.back navigate={~p"/notes"}>Back to notes diff --git a/lib/chiya_web/controllers/note_html/note_form.html.heex b/lib/chiya_web/controllers/note_html/note_form.html.heex new file mode 100644 index 0000000..0bf7664 --- /dev/null +++ b/lib/chiya_web/controllers/note_html/note_form.html.heex @@ -0,0 +1,14 @@ +<.simple_form :let={f} for={@changeset} action={@action}> + <.error :if={@changeset.action}> + Oops, something went wrong! Please check the errors below. + + <.input field={f[:name]} type="text" label="Name" /> + <.input field={f[:content]} type="text" label="Content" /> + <.input field={f[:slug]} type="text" label="Slug" /> + <.input field={f[:published_at]} type="datetime-local" label="Published at" /> + <.input field={f[:kind]} type="text" label="Kind" /> + <.input field={f[:url]} type="text" label="Url" /> + <:actions> + <.button>Save Note + + diff --git a/lib/chiya_web/controllers/note_html/show.html.heex b/lib/chiya_web/controllers/note_html/show.html.heex new file mode 100644 index 0000000..3972a51 --- /dev/null +++ b/lib/chiya_web/controllers/note_html/show.html.heex @@ -0,0 +1,20 @@ +<.header> + Note <%= @note.id %> + <:subtitle>This is a note record from your database. + <:actions> + <.link href={~p"/notes/#{@note}/edit"}> + <.button>Edit note + + + + +<.list> + <:item title="Name"><%= @note.name %> + <:item title="Content"><%= @note.content %> + <:item title="Slug"><%= @note.slug %> + <:item title="Published at"><%= @note.published_at %> + <:item title="Kind"><%= @note.kind %> + <:item title="Url"><%= @note.url %> + + +<.back navigate={~p"/notes"}>Back to notes diff --git a/lib/chiya_web/router.ex b/lib/chiya_web/router.ex index f2b8b34..2ce928e 100644 --- a/lib/chiya_web/router.ex +++ b/lib/chiya_web/router.ex @@ -23,6 +23,7 @@ defmodule ChiyaWeb.Router do get "/", PageController, :home resources "/channels", ChannelController + resources "/notes", NoteController end # Other scopes may use custom stacks. diff --git a/priv/repo/migrations/20230305162242_create_notes.exs b/priv/repo/migrations/20230305162242_create_notes.exs new file mode 100644 index 0000000..feeec79 --- /dev/null +++ b/priv/repo/migrations/20230305162242_create_notes.exs @@ -0,0 +1,18 @@ +defmodule Chiya.Repo.Migrations.CreateNotes do + use Ecto.Migration + + def change do + create table(:notes) do + add :name, :string + add :content, :text + add :slug, :string + add :published_at, :naive_datetime + add :kind, :string + add :url, :string + + timestamps() + end + + create unique_index(:notes, [:slug]) + end +end diff --git a/test/chiya/notes_test.exs b/test/chiya/notes_test.exs new file mode 100644 index 0000000..fe33d97 --- /dev/null +++ b/test/chiya/notes_test.exs @@ -0,0 +1,69 @@ +defmodule Chiya.NotesTest do + use Chiya.DataCase + + alias Chiya.Notes + + describe "notes" do + alias Chiya.Notes.Note + + import Chiya.NotesFixtures + + @invalid_attrs %{content: nil, kind: nil, name: nil, published_at: nil, slug: nil, url: nil} + + test "list_notes/0 returns all notes" do + note = note_fixture() + assert Notes.list_notes() == [note] + end + + test "get_note!/1 returns the note with given id" do + note = note_fixture() + assert Notes.get_note!(note.id) == note + end + + test "create_note/1 with valid data creates a note" do + valid_attrs = %{content: "some content", kind: "some kind", name: "some name", published_at: ~N[2023-03-04 16:22:00], slug: "some slug", url: "some url"} + + assert {:ok, %Note{} = note} = Notes.create_note(valid_attrs) + assert note.content == "some content" + assert note.kind == "some kind" + assert note.name == "some name" + assert note.published_at == ~N[2023-03-04 16:22:00] + assert note.slug == "some slug" + assert note.url == "some url" + end + + test "create_note/1 with invalid data returns error changeset" do + assert {:error, %Ecto.Changeset{}} = Notes.create_note(@invalid_attrs) + end + + test "update_note/2 with valid data updates the note" do + note = note_fixture() + update_attrs = %{content: "some updated content", kind: "some updated kind", name: "some updated name", published_at: ~N[2023-03-05 16:22:00], slug: "some updated slug", url: "some updated url"} + + assert {:ok, %Note{} = note} = Notes.update_note(note, update_attrs) + assert note.content == "some updated content" + assert note.kind == "some updated kind" + assert note.name == "some updated name" + assert note.published_at == ~N[2023-03-05 16:22:00] + assert note.slug == "some updated slug" + assert note.url == "some updated url" + end + + test "update_note/2 with invalid data returns error changeset" do + note = note_fixture() + assert {:error, %Ecto.Changeset{}} = Notes.update_note(note, @invalid_attrs) + assert note == Notes.get_note!(note.id) + end + + test "delete_note/1 deletes the note" do + note = note_fixture() + assert {:ok, %Note{}} = Notes.delete_note(note) + assert_raise Ecto.NoResultsError, fn -> Notes.get_note!(note.id) end + end + + test "change_note/1 returns a note changeset" do + note = note_fixture() + assert %Ecto.Changeset{} = Notes.change_note(note) + end + end +end diff --git a/test/chiya_web/controllers/note_controller_test.exs b/test/chiya_web/controllers/note_controller_test.exs new file mode 100644 index 0000000..4060f15 --- /dev/null +++ b/test/chiya_web/controllers/note_controller_test.exs @@ -0,0 +1,84 @@ +defmodule ChiyaWeb.NoteControllerTest do + use ChiyaWeb.ConnCase + + import Chiya.NotesFixtures + + @create_attrs %{content: "some content", kind: "some kind", name: "some name", published_at: ~N[2023-03-04 16:22:00], slug: "some slug", url: "some url"} + @update_attrs %{content: "some updated content", kind: "some updated kind", name: "some updated name", published_at: ~N[2023-03-05 16:22:00], slug: "some updated slug", url: "some updated url"} + @invalid_attrs %{content: nil, kind: nil, name: nil, published_at: nil, slug: nil, url: nil} + + describe "index" do + test "lists all notes", %{conn: conn} do + conn = get(conn, ~p"/notes") + assert html_response(conn, 200) =~ "Listing Notes" + end + end + + describe "new note" do + test "renders form", %{conn: conn} do + conn = get(conn, ~p"/notes/new") + assert html_response(conn, 200) =~ "New Note" + end + end + + describe "create note" do + test "redirects to show when data is valid", %{conn: conn} do + conn = post(conn, ~p"/notes", note: @create_attrs) + + assert %{id: id} = redirected_params(conn) + assert redirected_to(conn) == ~p"/notes/#{id}" + + conn = get(conn, ~p"/notes/#{id}") + assert html_response(conn, 200) =~ "Note #{id}" + end + + test "renders errors when data is invalid", %{conn: conn} do + conn = post(conn, ~p"/notes", note: @invalid_attrs) + assert html_response(conn, 200) =~ "New Note" + end + end + + describe "edit note" do + setup [:create_note] + + test "renders form for editing chosen note", %{conn: conn, note: note} do + conn = get(conn, ~p"/notes/#{note}/edit") + assert html_response(conn, 200) =~ "Edit Note" + end + end + + describe "update note" do + setup [:create_note] + + test "redirects when data is valid", %{conn: conn, note: note} do + conn = put(conn, ~p"/notes/#{note}", note: @update_attrs) + assert redirected_to(conn) == ~p"/notes/#{note}" + + conn = get(conn, ~p"/notes/#{note}") + assert html_response(conn, 200) =~ "some updated content" + end + + test "renders errors when data is invalid", %{conn: conn, note: note} do + conn = put(conn, ~p"/notes/#{note}", note: @invalid_attrs) + assert html_response(conn, 200) =~ "Edit Note" + end + end + + describe "delete note" do + setup [:create_note] + + test "deletes chosen note", %{conn: conn, note: note} do + conn = delete(conn, ~p"/notes/#{note}") + assert redirected_to(conn) == ~p"/notes" + + assert_error_sent 404, fn -> + get(conn, ~p"/notes/#{note}") + end + end + end + + defp create_note(_) do + note = note_fixture() + %{note: note} + end +end diff --git a/test/support/fixtures/notes_fixtures.ex b/test/support/fixtures/notes_fixtures.ex new file mode 100644 index 0000000..43381e1 --- /dev/null +++ b/test/support/fixtures/notes_fixtures.ex @@ -0,0 +1,30 @@ +defmodule Chiya.NotesFixtures do + @moduledoc """ + This module defines test helpers for creating + entities via the `Chiya.Notes` context. + """ + + @doc """ + Generate a unique note slug. + """ + def unique_note_slug, do: "some slug#{System.unique_integer([:positive])}" + + @doc """ + Generate a note. + """ + def note_fixture(attrs \\ %{}) do + {:ok, note} = + attrs + |> Enum.into(%{ + content: "some content", + kind: "some kind", + name: "some name", + published_at: ~N[2023-03-04 16:22:00], + slug: unique_note_slug(), + url: "some url" + }) + |> Chiya.Notes.create_note() + + note + end +end