create simple posts

This commit is contained in:
sloane 2025-02-22 12:35:38 -05:00
parent ef3d6d8b7a
commit d02833e472
Signed by: sloanelybutsurely
SSH key fingerprint: SHA256:8SBnwhl+RY3oEyQxy1a9wByPzxWM0x+/Ejc+sIlY5qQ
10 changed files with 171 additions and 7 deletions

25
lib/cms/posts.ex Normal file
View file

@ -0,0 +1,25 @@
defmodule CMS.Posts do
@moduledoc false
alias CMS.Posts.Post
alias CMS.Repo
def create_post(attrs) do
%Post{}
|> Post.changeset(attrs)
|> Repo.insert()
end
def update_post(post, attrs) do
post
|> Post.changeset(attrs)
|> Repo.update()
end
def get_post!(id) do
Repo.get!(Post, id)
end
def list_posts do
Repo.all(Post)
end
end

20
lib/cms/posts/post.ex Normal file
View file

@ -0,0 +1,20 @@
defmodule CMS.Posts.Post do
@moduledoc false
use Ecto.Schema
import Ecto.Changeset, warn: false
@primary_key {:id, :binary_id, autogenerate: true}
schema "posts" do
field :title, :string
field :contents, :string
timestamps()
end
def changeset(%__MODULE__{} = post, attrs \\ %{}) do
post
|> cast(attrs, [:title, :contents])
|> validate_required([:contents])
end
end

View file

@ -1,11 +1,14 @@
<div :if={@admin?} class="flex flex-row justify-between py-1 px-3 mb-2 border-b border-slate-100">
<div
:if={@admin?}
class="flex flex-row justify-between py-1 px-3 md:mb-2 border-b border-slate-100"
>
<section class="flex flex-row gap-x-2">
<div class="pr-2 border-r border-slate-100">
<.link navigate={~p"/admin"} class="font-bold">admin mode</.link>
</div>
<nav>
<ul class="flex flex-row">
<.link href="#" class="hover:underline">new post</.link>
<.link href={~p"/admin/posts/new"} class="hover:underline">new post</.link>
</ul>
</nav>
</section>
@ -21,12 +24,11 @@
<.link href={~p"/"} class="font-bold hover:underline">sloanelybutsurely.com</.link>
<nav>
<ul>
<li><.link href={~p"/writing"} class="hover:underline">writing</.link></li>
<li><.link href={~p"/microblog"} class="hover:underline">microblog</.link></li>
<li><.link href={~p"/posts"} class="hover:underline">writing</.link></li>
</ul>
</nav>
</section>
<main class="p-2">
<main class="p-2 w-full">
{@inner_content}
</main>
</div>

View file

@ -0,0 +1,21 @@
defmodule CMSWeb.PostController do
use CMSWeb, :controller
alias CMS.Posts
def index(conn, _params) do
posts = Posts.list_posts()
conn
|> assign(:posts, posts)
|> render(:index)
end
def show(conn, %{"post_id" => post_id}) do
post = Posts.get_post!(post_id)
conn
|> assign(:post, post)
|> render(:show)
end
end

View file

@ -0,0 +1,6 @@
defmodule CMSWeb.PostHTML do
@moduledoc false
use CMSWeb, :html
embed_templates "post_html/*"
end

View file

@ -0,0 +1,6 @@
<%= for post <- @posts do %>
<article id={"post-#{post.id}"}>
<h2><.link href={~p"/posts/#{post}"}>{post.title}</.link></h2>
<p>{post.contents}</p>
</article>
<% end %>

View file

@ -0,0 +1,5 @@
<header class="flex flex-row justify-between">
<h1>{@post.title}</h1>
<.link :if={@admin?} href={~p"/admin/posts/#{@post}"}>edit</.link>
</header>
<p>{@post.contents}</p>

View file

@ -0,0 +1,62 @@
defmodule CMSWeb.PostLive do
@moduledoc false
use CMSWeb, :live_view
alias CMS.Posts
alias CMS.Posts.Post
@impl true
def handle_params(_params, _uri, %{assigns: %{live_action: :new}} = socket) do
post = %Post{}
changeset = Post.changeset(post)
socket = assign(socket, post: post, form: to_form(changeset))
{:noreply, socket}
end
def handle_params(%{"post_id" => post_id}, _uri, %{assigns: %{live_action: :edit}} = socket) do
post = Posts.get_post!(post_id)
changeset = Post.changeset(post)
socket = assign(socket, post: post, form: to_form(changeset))
{:noreply, socket}
end
@impl true
def handle_event("save_post", %{"post" => attrs}, %{assigns: %{live_action: :new}} = socket) do
socket =
case Posts.create_post(attrs) do
{:ok, post} -> push_navigate(socket, to: ~p"/admin/posts/#{post}")
{:error, changeset} -> assign(socket, form: to_form(changeset))
end
{:noreply, socket}
end
def handle_event("save_post", %{"post" => attrs}, %{assigns: %{post: post, live_action: :edit}} = socket) do
socket =
case Posts.update_post(post, attrs) do
{:ok, post} ->
assign(socket, post: post, form: post |> Post.changeset() |> to_form())
{:error, changeset} ->
assign(socket, form: to_form(changeset))
end
{:noreply, socket}
end
@impl true
def render(assigns) do
~H"""
<.form for={@form} class="flex flex-col" phx-submit="save_post">
<input type="text" id={@form[:title].id} name={@form[:title].name} value={@form[:title].value} />
<textarea id={@form[:contents].id} name={@form[:contents].name}>{@form[:contents].value} </textarea>
<button type="submit" class="self-end">save</button>
</.form>
"""
end
end

View file

@ -32,8 +32,9 @@ defmodule CMSWeb.Router do
pipe_through :supports_admin_action
get "/", PageController, :home
get "/writing", PageController, :writing
get "/microblog", PageController, :microblog
get "/posts", PostController, :index
get "/posts/:post_id", PostController, :show
live "/sign-in", AdminLoginLive
post "/admin/session/create", AdminSessionController, :create
@ -45,6 +46,9 @@ defmodule CMSWeb.Router do
pipe_through :requires_admin
live "/", AdminLive
live "/posts/new", PostLive, :new
live "/posts/:post_id", PostLive, :edit
end
end

View file

@ -0,0 +1,13 @@
defmodule CMS.Repo.Migrations.AddPostsTable do
use Ecto.Migration
def change do
create table(:posts, primary_key: false) do
add :id, :uuid, primary_key: true
add :title, :text
add :contents, :text, null: false, default: ""
timestamps()
end
end
end