Add editable pages

This commit is contained in:
2023-11-18 11:01:14 +11:00
parent 484259fab1
commit 53434423fd
25 changed files with 369 additions and 5 deletions

15
app/relations/pages.rb Normal file
View File

@@ -0,0 +1,15 @@
# frozen_string_literal: true
module Adamantium
module Relations
class Pages < ROM::Relation[:sql]
schema :pages, infer: true
auto_struct(true)
def published
where(self[:published_at] <= Time.now)
end
end
end
end

11
app/repos/page_repo.rb Normal file
View File

@@ -0,0 +1,11 @@
module Adamantium
module Repos
class PageRepo < Adamantium::Repo[:pages]
def fetch!(slug:)
pages
.published
.where(slug: slug).one!
end
end
end
end

View File

@@ -2,6 +2,7 @@
- context.content_for(:highlight_code, false)
article class="mb-12 prose dark:prose-invert max-w-prose mx-auto text-gray-800 dark:text-gray-200 prose-em:font-bold prose-em:not-italic prose-em:bg-blue-600 prose-em:px-1 prose-em:rounded prose-a:text-blue-600 prose-a:dark:text-indigo-300 prose-a:p-0.5 prose-a:rounded-sm prose-a:no-underline hover:prose-a:underline prose-em:text-blue-100"
h1= page_name
== page_content
div class="max-w-screen-md mx-auto border-t border-solid border-gray-200 dark:border-gray-600"

View File

@@ -2,15 +2,21 @@ module Adamantium
module Views
module Pages
class Show < Adamantium::View
include Deps[renderer: "renderers.markdown"]
include Deps["repos.page_repo", renderer: "renderers.markdown"]
expose :page_content do |slug:|
markdown_content = File.read("app/content/pages/#{slug}.md")
renderer.call(content: markdown_content)
expose :page_content do |page|
renderer.call(content: page.content)
rescue Errno::ENOENT
renderer.call(content: "## Page not found")
end
expose :page_name do |page|
page.name
end
private_expose :page do |slug:|
page_repo.fetch!(slug: slug)
end
end
end
end

View File

@@ -0,0 +1,15 @@
# frozen_string_literal: true
ROM::SQL.migration do
change do
create_table :pages do
primary_key :id
column :name, :text, null: false
column :content, :text, null: false
column :slug, :text, null: false
column :published_at, :date
index :slug, unique: true
end
end
end

View File

@@ -0,0 +1,13 @@
module Admin
module Actions
module Pages
class Archive < Action
include Deps["repos.page_repo"]
def handle(req, res)
page_repo.archive(slug: req.params[:slug])
end
end
end
end
end

View File

@@ -0,0 +1,15 @@
module Admin
module Actions
module Pages
class Create < Action
include Deps["commands.pages.create"]
def handle(req, res)
create.call(page: req.params[:page])
res.redirect_to "/admin/pages"
end
end
end
end
end

View File

@@ -0,0 +1,13 @@
module Admin
module Actions
module Pages
class Delete < Action
include Deps["repos.page_repo"]
def handle(req, res)
page_repo.delete(id: req.params[:slug])
end
end
end
end
end

View File

@@ -0,0 +1,13 @@
module Admin
module Actions
module Pages
class Edit < Action
include Deps["views.pages.edit"]
def handle(req, res)
res.render edit, slug: req.params[:slug]
end
end
end
end
end

View File

@@ -0,0 +1,13 @@
module Admin
module Actions
module Pages
class Index < Action
include Deps["views.pages.index"]
def handle(req, res)
res.render index
end
end
end
end
end

View File

@@ -0,0 +1,13 @@
module Admin
module Actions
module Pages
class New < Action
include Deps["views.pages.new"]
def handle(req, res)
res.render new
end
end
end
end
end

View File

@@ -0,0 +1,13 @@
module Admin
module Actions
module Pages
class Publish < Action
include Deps["repos.page_repo"]
def handle(req, res)
page_repo.publish(slug: req.params[:slug])
end
end
end
end
end

View File

@@ -0,0 +1,14 @@
module Admin
module Actions
module Pages
class Update < Action
include Deps["commands.pages.update"]
def handle(req, res)
update.call(page: req.params[:page])
res.redirect "/admin/pages/#{req.params[:page][:slug]}/edit"
end
end
end
end
end

View File

@@ -0,0 +1,15 @@
require "down"
module Admin
module Commands
module Pages
class Create
include Deps["repos.page_repo"]
def call(page:)
page_repo.create(page)
end
end
end
end
end

View File

@@ -0,0 +1,17 @@
require "down"
module Admin
module Commands
module Pages
class Update
include Deps["repos.page_repo"]
def call(page:)
id = page_repo.find(slug: page[:slug]).id
page_repo.update(id, page)
end
end
end
end
end

View File

@@ -11,6 +11,15 @@ module Admin
get "/", to: Auth.call(action: "index")
get "/pages", to: Auth.call(action: "pages.index")
get "/pages/new", to: Auth.call(action: "pages.new")
get "/pages/:slug/edit", to: Auth.call(action: "pages.edit")
post "/pages/create", to: Auth.call(action: "pages.create")
post "/pages/:slug", to: Auth.call(action: "pages.update")
post "/pages/:slug/archive", to: Auth.call(action: "pages.archive")
post "/pages/:slug/publish", to: Auth.call(action: "pages.publish")
delete "/pages/:slug", to: Auth.call(action: "pages.delete")
get "/tags", to: Auth.call(action: "tags.index")
delete "/tags/:id", to: Auth.call(action: "tags.delete")

View File

@@ -0,0 +1,31 @@
require "time_math"
module Admin
module Repos
class PageRepo < Adamantium::Repo[:pages]
commands :create, update: :by_pk
def list
pages
.order(Sequel.lit("published_at desc"))
.to_a
end
def find(slug:)
pages.where(slug: slug).one!
end
def delete(slug:)
pages.where(slug: slug).delete
end
def publish(slug:)
pages.where(slug: slug).update(published_at: Time.now)
end
def archive(slug:)
pages.where(slug: slug).update(published_at: nil)
end
end
end
end

View File

@@ -3,6 +3,8 @@ div class="mb-12 prose dark:prose-invert max-w-prose mx-auto text-gray-800 dark:
div class="max-w-prose mx-auto prose dark:prose-invert"
ul
li
a href="/admin/pages" Pages
li
a href="/admin/posts" Posts
li

View File

@@ -0,0 +1,18 @@
article class="mb-12 prose dark:prose-invert max-w-prose mx-auto text-gray-800 dark:text-gray-200 prose-a:text-blue-600 prose-a:no-underline hover:prose-a:underline prose-img:rounded"
form action="/admin/pages/#{page.slug}" method="POST"
div class="mb-4"
label for="name" Name
input type="text" id="name" name="page[name]" class="text-gray-800 w-full border-blue-200 border-2 rounded p-2" value=page.name
div class="mb-4"
label for="slug" slug
input type="text" id="slug" name="page[slug]" class="text-gray-800 w-full border-blue-200 border-2 rounded p-2" value=page.slug
div class="mb-4"
label for="body" Body
textarea name="page[content]" id="body" class="text-gray-800 w-full border-blue-200 border-2 rounded p-2" x-data="{ resize: () => { $el.style.height = '5px'; $el.style.height = $el.scrollHeight + 'px' } }" x-init="resize()" @input="resize()"
== markdown_content
div class="mb-4"
label for="published_at" Publish date
input type="date" id="published_at" name="page[published_at]" class="text-gray-800 w-full border-blue-200 border-2 rounded p-2" value=page.published_at
button class="rounded bg-blue-100 hover:bg-blue-200 text-blue-600 px-2 hover:cursor-pointer" type="submit"
= "Update"

View File

@@ -0,0 +1,51 @@
div class="mb-12 prose dark:prose-invert max-w-prose mx-auto text-gray-800 dark:text-gray-200"
h1 Admin // Pages
div class="max-w-prose mx-auto" x-data="{ activeTab: 0 }"
div class="mb-4"
a href="/admin/pages/new" class="text-gray-200 cursor-pointer p-2 border-2 mr-2 rounded border-blue-400 bg-blue-400 text-blue-900" New page
div class="flex"
a href="#" class="text-gray-200 cursor-pointer p-2 border-2 mr-2 rounded border-blue-400" :class="{ 'bg-blue-400 text-blue-900': activeTab === 0 }" @click="activeTab = 0" class="tab-control" Published
a href="#" class="text-gray-200 cursor-pointer p-2 border-2 rounded border-blue-400" :class="{ 'bg-blue-400 text-blue-900': activeTab === 1 }" @click="activeTab = 1" class="tab-control" Un published
table class="prose dark:prose-invert table-auto prose-a:text-blue-600 prose-a:no-underline"
thead
th Details
th Date
th colspan="2" Actions
tbody class="{ 'active': activeTab === 0 }" x-show.transition.in.opacity.duration.600="activeTab === 0"
- published_pages.each do |page|
tr id="post-#{page.slug}"
td
div
= page.name
a class="no-underline" href="/#{page.slug}"
small class="text-gray-400 dark:text-gray-600" = page.slug
td
= page.published_at&.strftime("%d %b %Y")
td
a href="/admin/pages/#{page.slug}/edit" edit
td
button class="text-red-600" hx-delete="/admin/pages/#{page.slug}" hx-target="#post-#{page.slug}" delete
td
button hx-post="/admin/pages/#{page.slug}/archive" unpublish
tbody class="{ 'active': activeTab === 1 }" x-show.transition.in.opacity.duration.600="activeTab === 1"
- unpublished_pages.each do |page|
tr id="post-#{page.slug}"
td
div
= page.name
a class="no-underline" href="/#{page.slug}"
small class="text-gray-400 dark:text-gray-600" = page.slug
td
= page.published_at&.strftime("%d %b %Y")
td
a href="/admin/pages/#{page.slug}/edit" edit
td
button class="text-red-600" hx-delete="/admin/pages/#{page.slug}" hx-target="#post-#{page.slug}" delete
td
button hx-post="/admin/pages/#{page.slug}/publish" publish
div class="max-w-screen-md mx-auto border-t border-solid border-gray-200 dark:border-gray-600"

View File

@@ -0,0 +1,17 @@
article class="mb-12 prose dark:prose-invert max-w-prose mx-auto text-gray-800 dark:text-gray-200 prose-a:text-blue-600 prose-a:no-underline hover:prose-a:underline prose-img:rounded"
form action="/admin/pages/create" method="POST"
div class="mb-4"
label for="name" Name
input type="text" id="name" name="page[name]" class="text-gray-800 w-full border-blue-200 border-2 rounded p-2"
div class="mb-4"
label for="slug" slug
input type="text" id="slug" name="page[slug]" class="text-gray-800 w-full border-blue-200 border-2 rounded p-2"
div class="mb-4"
label for="body" Body
textarea name="page[content]" id="body" class="text-gray-800 w-full border-blue-200 border-2 rounded p-2" x-data="{ resize: () => { $el.style.height = '5px'; $el.style.height = $el.scrollHeight + 'px' } }" x-init="resize()" @input="resize()"
div class="mb-4"
label for="published_at" Publish date
input type="date" id="published_at" name="page[published_at]" class="text-gray-800 w-full border-blue-200 border-2 rounded p-2"
button class="rounded bg-blue-100 hover:bg-blue-200 text-blue-600 px-2 hover:cursor-pointer" type="submit"
= "Create"

View File

@@ -0,0 +1,19 @@
require "reverse_markdown"
module Admin
module Views
module Pages
class Edit < Admin::View
include Deps["repos.page_repo"]
expose :page do |slug:|
page_repo.find(slug: slug)
end
expose :markdown_content do |page|
page.content
end
end
end
end
end

View File

@@ -0,0 +1,21 @@
module Admin
module Views
module Pages
class Index < Admin::View
include Deps["repos.page_repo"]
expose :published_pages do |pages|
pages[0]
end
expose :unpublished_pages do |pages|
pages[1]
end
expose :pages do
page_repo.list.partition { |p| p.published_at }
end
end
end
end
end

View File

@@ -0,0 +1,9 @@
module Admin
module Views
module Pages
class New < Admin::View
end
end
end
end