Editable content #229

Merged
greg merged 15 commits from feature/186-content_editing into master 2025-05-30 11:14:51 +00:00
13 changed files with 92 additions and 59 deletions
Showing only changes of commit 6014134396 - Show all commits

View File

@ -0,0 +1,30 @@
<div class="inline-block text-left" data-controller="modal" data-action="keydown.esc->modal#close">
<button class="btn-md btn-outline text-red-600" data-action="click->modal#open" title="Edit">
<%= content || "Edit" %>
</button>
<%= render ModalComponent.new(show_close_button: false) do %>
<%= form_with model: [:admin, @editable_content],
html: { autocomplete: "off" } do |form| %>
<%= form.hidden_field :redirect_to, value: @redirect_to %>
<p class="mb-2">
<%= form.label :content, @editable_content.key.capitalize, class: 'font-bold' %>
</p>
<% if @editable_content.rich_text %>
<p>
<%= form.textarea :content, class: "md:w-[56rem] md:h-[28rem]" %>
</p>
<p class="text-right">
<%= form.submit "Save", class: "ml-2 btn-md btn-blue" %>
</p>
<% else %>
<p class="">
<%= form.text_field :content, class: "w-80" %>
</p>
<p>
<%= form.submit "Save", class: "btn-md btn-blue w-full" %>
</p>
<% end %>
<% end %>
<% end %>
</div>

View File

@ -0,0 +1,6 @@
class EditContentButtonComponent < ViewComponent::Base
def initialize(context:, key:, rich_text: false, redirect_to: nil)
@editable_content = EditableContent.find_or_create_by(context:, key:, rich_text:)
@redirect_to = redirect_to
end
end

View File

@ -0,0 +1,9 @@
<% if @editable_content.has_content? %>
<% if @editable_content.rich_text %>
<%= helpers.markdown_to_html @editable_content.content %>
<% else %>
<%= @editable_content.content %>
<% end %>
<% else %>
<%= @default %>
<% end %>

View File

@ -0,0 +1,6 @@
class EditableContentComponent < ViewComponent::Base
def initialize(context:, key:, rich_text: false, default: nil)
@editable_content = EditableContent.find_or_create_by(context:, key:, rich_text:)
@default = default
end
end

View File

@ -16,8 +16,14 @@ class Admin::EditableContentsController < Admin::BaseController
end
def update
return_to = params[:editable_content][:redirect_to].presence
if @editable_content.update(content_params)
render json: { status: "success", message: "Content updated" }, status: :ok
if return_to
redirect_to return_to
else
render status: :ok
end
else
render :edit, status: :unprocessable_entity
end

View File

@ -4,12 +4,6 @@ class Contributions::OtherController < ApplicationController
# GET /contributions/other
def index
@content_title = EditableContent.find_or_create_by(
path: "contributions/other", key: "title"
)
@content_body = EditableContent.find_or_create_by(
path: "contributions/other", key: "body", rich_text: true
)
@current_section = :contributions
end

View File

@ -15,6 +15,10 @@ module ApplicationHelper
tag.span text, class: "inline-flex items-center rounded-full bg-#{color}-100 px-2.5 py-0.5 text-xs font-medium text-#{color}-800"
end
def markdown_to_html(string)
raw Kramdown::Document.new(string, { input: "GFM" }).to_html
end
def image_url_for(attachment)
return s3_image_url(attachment) if Setting.s3_enabled?

View File

@ -1,10 +0,0 @@
module EditableContentHelper
def editable_content_for(path, key, default: nil, create_rich: false)
@content = EditableContent.find_by(path: "contributions/other", key: key)
@content.content.present? ? @content.content : default
end
def markdown_to_html(string)
raw Kramdown::Document.new(string, { input: "GFM" }).to_html
end
end

View File

@ -1,4 +1,12 @@
class EditableContent < ApplicationRecord
validates :key, presence: true,
uniqueness: { scope: :path }
uniqueness: { scope: :context }
def has_content?
content.present?
end
def is_empty?
content.blank?
end
end

View File

@ -1,42 +1,20 @@
<%= render HeaderComponent.new(title: "Contributions") %>
<%= render MainWithTabnavComponent.new(tabnav_partial: "shared/tabnav_contributions") do %>
<% if @edit_content %>
<section>
<%= form_with model: [:admin, @content_title] do |form| %>
<p class="mb-2">
<%= form.label :content, @content_title.key.capitalize, class: 'font-bold' %>
</p>
<p class="flex gap-1">
<%= form.text_field :content %>
<%# <%= form.submit "Save", class: "btn-md btn-blue" %>
<%= button_tag type: 'submit', name: nil, title: "Save", class: 'btn-md btn-icon btn-outline' do %>
<%= render partial: "icons/save", locals: { custom_class: "text-blue-600 h-4 w-4 inline" } %>
<% end %>
</p>
<% end %>
</section>
<section>
<%= form_with model: [:admin, @content_body] do |form| %>
<p class="mb-2">
<%= form.label :content, @content_body.key.capitalize, class: 'font-bold' %>
</p>
<p>
<%= form.textarea :content, class: "w-full h-96" %>
</p>
<p class="text-right">
<%= link_to 'Cancel', request.path, class: 'btn-md btn-gray' %>
<%= form.submit "Save", class: "ml-2 btn-md btn-blue" %>
</p>
<% end %>
</section>
<% else %>
<section>
<% if @content_body.content.present? %>
<%= markdown_to_html @content_body.content %>
<% else %>
No content yet
<% end %>
</section>
<% end %>
<section>
<%= render EditableContentComponent.new(
context: "contributions/other", key: "body", rich_text: true,
default: "No content yet") %>
<% if current_user.is_admin? %>
<div class="mt-8 pt-6 border-t border-gray-200 text-right">
<%= render EditContentButtonComponent.new(
context: "contributions/other", key: "title",
redirect_to: request.path) do %>Edit title<% end %>
<%= render EditContentButtonComponent.new(
context: "contributions/other", key: "body", rich_text: true,
redirect_to: request.path) do %>Edit content<% end %>
</div>
<% end %>
</section>
<% end %>

View File

@ -6,7 +6,9 @@
active: current_page?(contributions_donations_path)
) %>
<%= render TabnavLinkComponent.new(
name: editable_content_for("contributions/other", "title", default: "Other"),
name: render(EditableContentComponent.new(
context: "contributions/other", key: "title", default: "Other"
)),
path: contributions_other_path,
active: current_page?(contributions_other_path)
) %>

View File

@ -1,7 +1,7 @@
class CreateEditableContents < ActiveRecord::Migration[8.0]
def change
create_table :editable_contents do |t|
t.string :path
t.string :context
t.string :key
t.string :lang, default: "en"
t.text :content

View File

@ -65,7 +65,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_05_28_092931) do
end
create_table "editable_contents", force: :cascade do |t|
t.string "path"
t.string "context"
t.string "key"
t.string "lang", default: "en"
t.text "content"