Add editable pages
This commit is contained in:
15
app/relations/pages.rb
Normal file
15
app/relations/pages.rb
Normal 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
11
app/repos/page_repo.rb
Normal 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
|
@@ -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"
|
||||
|
@@ -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
|
||||
|
15
db/migrate/20231117222529_create_pages.rb
Normal file
15
db/migrate/20231117222529_create_pages.rb
Normal 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
|
13
slices/admin/actions/pages/archive.rb
Normal file
13
slices/admin/actions/pages/archive.rb
Normal 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
|
15
slices/admin/actions/pages/create.rb
Normal file
15
slices/admin/actions/pages/create.rb
Normal 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
|
13
slices/admin/actions/pages/delete.rb
Normal file
13
slices/admin/actions/pages/delete.rb
Normal 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
|
13
slices/admin/actions/pages/edit.rb
Normal file
13
slices/admin/actions/pages/edit.rb
Normal 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
|
13
slices/admin/actions/pages/index.rb
Normal file
13
slices/admin/actions/pages/index.rb
Normal 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
|
13
slices/admin/actions/pages/new.rb
Normal file
13
slices/admin/actions/pages/new.rb
Normal 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
|
13
slices/admin/actions/pages/publish.rb
Normal file
13
slices/admin/actions/pages/publish.rb
Normal 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
|
14
slices/admin/actions/pages/update.rb
Normal file
14
slices/admin/actions/pages/update.rb
Normal 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
|
15
slices/admin/commands/pages/create.rb
Normal file
15
slices/admin/commands/pages/create.rb
Normal 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
|
17
slices/admin/commands/pages/update.rb
Normal file
17
slices/admin/commands/pages/update.rb
Normal 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
|
@@ -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")
|
||||
|
||||
|
31
slices/admin/repos/page_repo.rb
Normal file
31
slices/admin/repos/page_repo.rb
Normal 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
|
@@ -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
|
||||
|
18
slices/admin/templates/pages/edit.html.slim
Normal file
18
slices/admin/templates/pages/edit.html.slim
Normal 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"
|
51
slices/admin/templates/pages/index.html.slim
Normal file
51
slices/admin/templates/pages/index.html.slim
Normal 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"
|
||||
|
||||
|
||||
|
17
slices/admin/templates/pages/new.html.slim
Normal file
17
slices/admin/templates/pages/new.html.slim
Normal 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"
|
19
slices/admin/views/pages/edit.rb
Normal file
19
slices/admin/views/pages/edit.rb
Normal 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
|
21
slices/admin/views/pages/index.rb
Normal file
21
slices/admin/views/pages/index.rb
Normal 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
|
9
slices/admin/views/pages/new.rb
Normal file
9
slices/admin/views/pages/new.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
module Admin
|
||||
module Views
|
||||
module Pages
|
||||
class New < Admin::View
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user