Compare commits

...

2 Commits

Author SHA1 Message Date
ab1490f472
Remove Kosmos name from wording
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
refs #222
2025-05-29 14:24:43 +04:00
6014134396
Finish MVP for content editing 2025-05-29 14:18:14 +04:00
21 changed files with 101 additions and 68 deletions

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 end
def update def update
return_to = params[:editable_content][:redirect_to].presence
if @editable_content.update(content_params) 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 else
render :edit, status: :unprocessable_entity render :edit, status: :unprocessable_entity
end end

View File

@ -4,12 +4,6 @@ class Contributions::OtherController < ApplicationController
# GET /contributions/other # GET /contributions/other
def index 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 @current_section = :contributions
end 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" 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 end
def markdown_to_html(string)
raw Kramdown::Document.new(string, { input: "GFM" }).to_html
end
def image_url_for(attachment) def image_url_for(attachment)
return s3_image_url(attachment) if Setting.s3_enabled? 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 class EditableContent < ApplicationRecord
validates :key, presence: true, validates :key, presence: true,
uniqueness: { scope: :path } uniqueness: { scope: :context }
def has_content?
content.present?
end
def is_empty?
content.blank?
end
end end

View File

@ -3,7 +3,7 @@
<%= render MainWithTabnavComponent.new(tabnav_partial: "shared/tabnav_contributions") do %> <%= render MainWithTabnavComponent.new(tabnav_partial: "shared/tabnav_contributions") do %>
<section> <section>
<p class="mb-12"> <p class="mb-12">
Your financial contributions to the development and upkeep of Kosmos Your financial contributions to the development and upkeep of our
software and services. software and services.
</p> </p>
</section> </section>

View File

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

View File

@ -115,7 +115,7 @@
class: "block h-full px-6 py-6 rounded-md" do %> class: "block h-full px-6 py-6 rounded-md" do %>
<h3 class="mb-3.5">Wiki</h3> <h3 class="mb-3.5">Wiki</h3>
<p class="text-gray-600"> <p class="text-gray-600">
Kosmos documentation and knowledge base Documentation and knowledge base
</p> </p>
<% end %> <% end %>
</div> </div>

View File

@ -4,7 +4,7 @@
<section> <section>
<% if @invitations_unused.any? %> <% if @invitations_unused.any? %>
<p class="mb-8"> <p class="mb-8">
Invite your friends to a Kosmos account by sharing an invitation URL with them: Invite your friends to register an account by sharing an invitation URL with them:
</p> </p>
<ul class="md:w-3/4"> <ul class="md:w-3/4">
<% @invitations_unused.each do |invitation| %> <% @invitations_unused.each do |invitation| %>

View File

@ -1,6 +1,6 @@
Hi <%= @user.display_name.presence || @user.cn %>, Hi <%= @user.display_name.presence || @user.cn %>,
New invitations have just been added to your Kosmos account, so you can invite more people to our cooperative services: New invitations have just been added to your account, so you can invite more people to our cooperative services:
<%= invitations_url %> <%= invitations_url %>

View File

@ -1,6 +1,6 @@
Hi <%= @user.display_name.presence || @user.cn %>, Hi <%= @user.display_name.presence || @user.cn %>,
You have just granted '<%= @auth.client_id %>' access to your Kosmos Storage, with the following permissions: You have just granted '<%= @auth.client_id %>' access to your remote storage, with the following permissions:
<% @permissions.each do |p| %> <% @permissions.each do |p| %>
* <%= p %> * <%= p %>

View File

@ -8,7 +8,7 @@
<section> <section>
<h3>Lightning Address</h3> <h3>Lightning Address</h3>
<p class="mb-6"> <p class="mb-6">
Your Kosmos user address is also a Your user address is also a
<a class="ks-text-link" href="https://lightningaddress.com/" target="_blank">Lightning Address</a>! <a class="ks-text-link" href="https://lightningaddress.com/" target="_blank">Lightning Address</a>!
The easiest way to receive sats is by just giving out your address: The easiest way to receive sats is by just giving out your address:
</p> </p>
@ -32,7 +32,7 @@
<section data-controller="modal" data-action="keydown.esc->modal#close"> <section data-controller="modal" data-action="keydown.esc->modal#close">
<h3>Wallet Apps</h3> <h3>Wallet Apps</h3>
<p> <p>
You can connect various wallet apps to your Kosmos account. This allows You can connect various wallet apps to your account. This allows
you to both receive and send sats. Any wallet that supports you to both receive and send sats. Any wallet that supports
<a href="https://bluewallet.io/lndhub/" class="ks-text-link" target="_blank">LNDHub</a> <a href="https://bluewallet.io/lndhub/" class="ks-text-link" target="_blank">LNDHub</a>
accounts should be able to add/import your account using our setup accounts should be able to add/import your account using our setup

View File

@ -3,7 +3,7 @@
<%= render MainSimpleComponent.new do %> <%= render MainSimpleComponent.new do %>
<section> <section>
<p class="mb-6"> <p class="mb-6">
Follow and interact with anyone on the open social web, from your Kosmos Mastodon account. Follow and interact with anyone on the open social web from your Mastodon account.
</p> </p>
</section> </section>
<section data-controller="modal" data-action="keydown.esc->modal#close"> <section data-controller="modal" data-action="keydown.esc->modal#close">

View File

@ -6,7 +6,9 @@
active: current_page?(contributions_donations_path) active: current_page?(contributions_donations_path)
) %> ) %>
<%= render TabnavLinkComponent.new( <%= 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, path: contributions_other_path,
active: current_page?(contributions_other_path) active: current_page?(contributions_other_path)
) %> ) %>

View File

@ -2,7 +2,7 @@
<%= render MainCompactComponent.new do %> <%= render MainCompactComponent.new do %>
<p> <p>
Hey there! You were invited to sign up for a Kosmos account by Hey there! You were invited to sign up for an account by
<strong><%= @invited_by_name %></strong>. <strong><%= @invited_by_name %></strong>.
</p> </p>
<p> <p>

View File

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

View File

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