admin improvements pt. 2
This commit is contained in:
parent
1675efb514
commit
0bcb8f0768
13 changed files with 124 additions and 55 deletions
lib
|
@ -1,4 +1,4 @@
|
|||
defmodule Core do
|
||||
@moduledoc false
|
||||
use Boundary, deps: [Schema], exports: [Accounts, Posts, Author]
|
||||
use Boundary, deps: [Schema], exports: [Accounts, Posts, Author, DateTime]
|
||||
end
|
||||
|
|
9
lib/core/date_time.ex
Normal file
9
lib/core/date_time.ex
Normal file
|
@ -0,0 +1,9 @@
|
|||
defmodule Core.DateTime do
|
||||
@local_time_zone "America/New_York"
|
||||
|
||||
def to_local_time(nil), do: nil
|
||||
|
||||
def to_local_time(date_time) do
|
||||
DateTime.shift_zone!(date_time, @local_time_zone)
|
||||
end
|
||||
end
|
|
@ -50,11 +50,15 @@ defmodule Core.Posts do
|
|||
|> Flop.validate_and_run(params, for: Schema.Post)
|
||||
end
|
||||
|
||||
def publish_date(%Schema.Post{published_at: nil}), do: nil
|
||||
def publish_date_time(%Schema.Post{published_at: nil}), do: nil
|
||||
|
||||
def publish_date(%Schema.Post{published_at: published_at}) do
|
||||
published_at
|
||||
|> DateTime.shift_zone!("America/New_York")
|
||||
def publish_date_time(%Schema.Post{published_at: published_at}) do
|
||||
Core.DateTime.to_local_time(published_at)
|
||||
end
|
||||
|
||||
def publish_date(%Schema.Post{} = post) do
|
||||
post
|
||||
|> publish_date_time()
|
||||
|> DateTime.to_date()
|
||||
end
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ defmodule Core.Posts.Post do
|
|||
def content_changeset(%Schema.Post{} = post, attrs) do
|
||||
changeset =
|
||||
post
|
||||
|> cast(attrs, [:tid, :kind, :slug, :title, :body])
|
||||
|> cast(attrs, [:tid, :kind, :slug, :title, :body, :deleted_at, :published_at])
|
||||
|> validate_required([:kind], message: "must have a kind")
|
||||
|> validate_required([:body], message: "must have a body")
|
||||
|
||||
|
@ -40,7 +40,9 @@ defmodule Core.Posts.Post do
|
|||
changeset = change(post)
|
||||
|
||||
if is_nil(get_field(changeset, :published_at)) do
|
||||
put_change(changeset, :published_at, published_at)
|
||||
changeset
|
||||
|> put_change(:deleted_at, nil)
|
||||
|> put_change(:published_at, published_at)
|
||||
else
|
||||
changeset
|
||||
end
|
||||
|
@ -50,7 +52,9 @@ defmodule Core.Posts.Post do
|
|||
changeset = change(post)
|
||||
|
||||
if is_nil(get_field(changeset, :deleted_at)) do
|
||||
put_change(changeset, :deleted_at, deleted_at)
|
||||
changeset
|
||||
|> put_change(:published_at, nil)
|
||||
|> put_change(:deleted_at, deleted_at)
|
||||
else
|
||||
changeset
|
||||
end
|
||||
|
@ -81,7 +85,12 @@ defmodule Core.Posts.Post do
|
|||
end
|
||||
|
||||
def default_order(query \\ base()) do
|
||||
order_by(query, [posts: p], desc: :inserted_at, desc: :updated_at)
|
||||
order_by(query, [posts: p],
|
||||
desc: :published_at,
|
||||
desc: :inserted_at,
|
||||
desc: :updated_at,
|
||||
desc: :id
|
||||
)
|
||||
end
|
||||
|
||||
def where_kind(query \\ base(), kind) do
|
||||
|
|
|
@ -5,11 +5,11 @@ defmodule Schema.Post do
|
|||
@derive {
|
||||
Flop.Schema,
|
||||
filterable: [],
|
||||
sortable: [:published_at, :id],
|
||||
sortable: [:published_at, :inserted_at, :updated_at, :id],
|
||||
pagination_types: [:first, :last],
|
||||
default_order: %{
|
||||
order_by: [:published_at, :id],
|
||||
order_directions: [:desc, :desc]
|
||||
order_by: [:published_at, :inserted_at, :updated_at, :id],
|
||||
order_directions: [:desc, :desc, :desc, :desc]
|
||||
},
|
||||
default_pagination_type: :first,
|
||||
default_limit: 20,
|
||||
|
|
|
@ -10,7 +10,8 @@ defmodule Web.CoreComponents do
|
|||
attr :name, :any
|
||||
attr :label, :string, default: nil
|
||||
attr :value, :any
|
||||
attr :type, :string, default: "text", values: ~w[text password textarea]
|
||||
attr :class, :string, default: nil
|
||||
attr :type, :string, default: "text", values: ~w[text password textarea datetime-local]
|
||||
attr :field, FormField
|
||||
attr :errors, :list, default: []
|
||||
attr :rest, :global, include: ~w[disabled form pattern placeholder readonly required]
|
||||
|
@ -36,9 +37,9 @@ defmodule Web.CoreComponents do
|
|||
|
||||
def input(%{type: "textarea"} = assigns) do
|
||||
~H"""
|
||||
<div>
|
||||
<div class={["flex flex-col", @class]}>
|
||||
<.label for={@id}>{@label}</.label>
|
||||
<textarea id={@id} name={@name}>{Phoenix.HTML.Form.normalize_value(@type, @value)}</textarea>
|
||||
<textarea id={@id} name={@name} class="h-80">{Phoenix.HTML.Form.normalize_value(@type, @value)}</textarea>
|
||||
<.error :for={error <- @errors}>{error}</.error>
|
||||
</div>
|
||||
"""
|
||||
|
@ -46,7 +47,7 @@ defmodule Web.CoreComponents do
|
|||
|
||||
def input(assigns) do
|
||||
~H"""
|
||||
<div>
|
||||
<div class="flex flex-col">
|
||||
<.label for={@id}>{@label}</.label>
|
||||
<input
|
||||
id={@id}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
>
|
||||
<h3 class="p-name u-url">{blog.title}</h3>
|
||||
<div class="text-gray-500 dt-published">
|
||||
<.timex value={blog.published_at} format="{Mfull} {D}" />
|
||||
<.timex value={Core.Posts.publish_date_time(blog)} format="{Mfull} {D}" />
|
||||
</div>
|
||||
</.link>
|
||||
</article>
|
||||
|
|
|
@ -3,10 +3,18 @@
|
|||
<header class="mb-4">
|
||||
<h1 class="p-name text-2xl font-bold mb-2">{@blog.title}</h1>
|
||||
<%= if @blog.published_at do %>
|
||||
<div class="text-sm text-gray-500 flex items-center">
|
||||
<div class="text-sm text-gray-500 flex items-center gap-x-2">
|
||||
<span class="p-author h-card">{Core.Author.get(:name)}</span>
|
||||
<span class="mx-2">·</span>
|
||||
<.timex value={@blog.published_at} format="{Mfull} {D}, {YYYY}" class="dt-published" />
|
||||
<span>·</span>
|
||||
<.timex
|
||||
value={Core.Posts.publish_date_time(@blog)}
|
||||
format="{Mfull} {D}, {YYYY}"
|
||||
class="dt-published"
|
||||
/>
|
||||
<%= if @current_user do %>
|
||||
<span>·</span>
|
||||
<.link navigate={~p"/admin/posts/#{@blog}"}>edit</.link>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</header>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
>
|
||||
<h3 class="p-name u-url">{blog.title}</h3>
|
||||
<div class="text-gray-500 dt-published">
|
||||
<.timex value={blog.published_at} format="{Mfull} {D}" />
|
||||
<.timex value={Core.Posts.publish_date_time(blog)} format="{Mfull} {D}" />
|
||||
</div>
|
||||
</.link>
|
||||
</article>
|
||||
|
|
|
@ -49,7 +49,7 @@ defmodule Web.StatusHTML do
|
|||
<span class="mx-2 text-sm text-gray-500">·</span>
|
||||
<div class="text-sm text-gray-500">
|
||||
<.timex
|
||||
value={@status.published_at}
|
||||
value={Core.Posts.publish_date_time(@status)}
|
||||
format="{relative}"
|
||||
formatter={:relative}
|
||||
class="dt-published"
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
</div>
|
||||
|
||||
<article class="h-entry p-4 border border-gray-200">
|
||||
<header class="flex items-center mb-3">
|
||||
<header class="flex mb-3 justify-between">
|
||||
<div class="h-card flex items-center gap-3 p-author">
|
||||
<%= if Core.Author.get(:avatar_url) do %>
|
||||
<img
|
||||
|
@ -34,6 +34,10 @@
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= if @current_user do %>
|
||||
<.link class="text-sm text-gray-500" navigate={~p"/admin/posts/#{@status}"}>edit</.link>
|
||||
<% end %>
|
||||
</header>
|
||||
|
||||
<div class="e-content my-4">
|
||||
|
@ -43,7 +47,7 @@
|
|||
<footer class="mt-4 border-t border-gray-200 pt-2 text-sm text-gray-500">
|
||||
<%= if @status.published_at do %>
|
||||
<.timex
|
||||
value={@status.published_at}
|
||||
value={Core.Posts.publish_date_time(@status)}
|
||||
format="{Mfull} {D}, {YYYY} at {h12}:{m} {AM}"
|
||||
class="dt-published"
|
||||
/>
|
||||
|
|
|
@ -53,7 +53,13 @@ defmodule Web.AdminPostLive do
|
|||
socket =
|
||||
case Core.Posts.create_or_update_post(post, attrs) do
|
||||
{:ok, post} ->
|
||||
form =
|
||||
post
|
||||
|> Core.Posts.change_post(%{})
|
||||
|> to_form()
|
||||
|
||||
socket
|
||||
|> assign(post: post, form: form)
|
||||
|> put_flash(:info, "post saved")
|
||||
|> push_patch(to: ~p"/admin/posts/#{post}", replace: true)
|
||||
|
||||
|
@ -68,7 +74,12 @@ defmodule Web.AdminPostLive do
|
|||
socket =
|
||||
case Core.Posts.publish_post(post) do
|
||||
{:ok, post} ->
|
||||
assign(socket, post: post)
|
||||
form =
|
||||
post
|
||||
|> Core.Posts.change_post(%{})
|
||||
|> to_form()
|
||||
|
||||
assign(socket, post: post, form: form)
|
||||
|
||||
_ ->
|
||||
socket
|
||||
|
@ -81,7 +92,12 @@ defmodule Web.AdminPostLive do
|
|||
socket =
|
||||
case Core.Posts.unpublish_post(post) do
|
||||
{:ok, post} ->
|
||||
assign(socket, post: post)
|
||||
form =
|
||||
post
|
||||
|> Core.Posts.change_post(%{})
|
||||
|> to_form()
|
||||
|
||||
assign(socket, post: post, form: form)
|
||||
|
||||
_ ->
|
||||
socket
|
||||
|
@ -94,7 +110,12 @@ defmodule Web.AdminPostLive do
|
|||
socket =
|
||||
case Core.Posts.delete_post(post) do
|
||||
{:ok, post} ->
|
||||
assign(socket, post: post)
|
||||
form =
|
||||
post
|
||||
|> Core.Posts.change_post(%{})
|
||||
|> to_form()
|
||||
|
||||
assign(socket, post: post, form: form)
|
||||
|
||||
_ ->
|
||||
socket
|
||||
|
@ -107,7 +128,12 @@ defmodule Web.AdminPostLive do
|
|||
socket =
|
||||
case Core.Posts.undelete_post(post) do
|
||||
{:ok, post} ->
|
||||
assign(socket, post: post)
|
||||
form =
|
||||
post
|
||||
|> Core.Posts.change_post(%{})
|
||||
|> to_form()
|
||||
|
||||
assign(socket, post: post, form: form)
|
||||
|
||||
_ ->
|
||||
socket
|
||||
|
|
|
@ -1,33 +1,41 @@
|
|||
<main>
|
||||
<header>
|
||||
<h1>{page_title(@post, @live_action)}</h1>
|
||||
</header>
|
||||
|
||||
<.form for={@form} phx-change="validate" phx-submit="save">
|
||||
<.input :if={@post.kind == :blog} type="text" field={@form[:title]} />
|
||||
<.form
|
||||
for={@form}
|
||||
phx-change="validate"
|
||||
phx-submit="save"
|
||||
class="flex flex-col md:flex-row p-4 gap-4"
|
||||
>
|
||||
<div class="flex-grow flex flex-col gap-y-4">
|
||||
<.input :if={@post.kind == :blog} type="text" label="title" field={@form[:title]} />
|
||||
<.input type="textarea" field={@form[:body]} label="body" class="md:flex-grow" />
|
||||
</div>
|
||||
<div class="md:w-80 flex flex-col gap-y-2">
|
||||
<.button type="submit" class="self-end">save</.button>
|
||||
<.link
|
||||
:if={@post.published_at}
|
||||
class="self-end"
|
||||
navigate={Web.Paths.public_post_path(@post)}
|
||||
target="_blank"
|
||||
>
|
||||
view
|
||||
</.link>
|
||||
<.input type="datetime-local" label="published (utc)" field={@form[:published_at]} />
|
||||
<%= if @post.published_at do %>
|
||||
<.button phx-click="unpublish" class="self-end">unpublish</.button>
|
||||
<% else %>
|
||||
<.button phx-click="publish" class="self-end">publish</.button>
|
||||
<% end %>
|
||||
<.input type="datetime-local" label="deleted (utc)" field={@form[:deleted_at]} />
|
||||
<%= if @post.deleted_at do %>
|
||||
<.button phx-click="undelete" class="self-end">undelete</.button>
|
||||
<% else %>
|
||||
<.button phx-click="delete" class="self-end">delete</.button>
|
||||
<% end %>
|
||||
<.input
|
||||
:if={@post.kind == :blog}
|
||||
label="slug"
|
||||
type="text"
|
||||
field={@form[:slug]}
|
||||
disabled={not update_slug?(@post)}
|
||||
/>
|
||||
<.input type="textarea" field={@form[:body]} />
|
||||
|
||||
<.button type="submit">save</.button>
|
||||
</.form>
|
||||
|
||||
<%= if @live_action == :edit do %>
|
||||
<div>
|
||||
<%= if @post.published_at do %>
|
||||
<.button phx-click="unpublish">unpublish</.button>
|
||||
<% else %>
|
||||
<.button phx-click="publish">publish</.button>
|
||||
<% end %>
|
||||
<%= if @post.deleted_at do %>
|
||||
<.button phx-click="undelete">undelete</.button>
|
||||
<% else %>
|
||||
<.button phx-click="delete">delete</.button>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</main>
|
||||
</div>
|
||||
</.form>
|
||||
|
|
Loading…
Reference in a new issue