spina-admin-conferences-blog 0.1.2 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -0
  3. data/app/components/spina/admin/conferences/blog/application_component.rb +13 -0
  4. data/app/components/spina/admin/conferences/blog/translations_component.html.erb +39 -0
  5. data/app/components/spina/admin/conferences/blog/translations_component.rb +44 -0
  6. data/app/controllers/spina/admin/conferences/blog/categories_controller.rb +6 -5
  7. data/app/controllers/spina/admin/conferences/blog/posts_controller.rb +5 -5
  8. data/app/models/spina/admin/conferences/blog/category.rb +3 -1
  9. data/app/models/spina/admin/conferences/blog/post.rb +4 -0
  10. data/app/views/layouts/default/application.html.erb +1 -0
  11. data/app/views/spina/admin/conferences/blog/categories/_category.html.erb +13 -0
  12. data/app/views/spina/admin/conferences/blog/categories/_form.html.erb +38 -0
  13. data/app/views/spina/admin/conferences/blog/categories/edit.html.erb +1 -0
  14. data/app/views/spina/admin/conferences/blog/categories/index.html.erb +19 -0
  15. data/app/views/spina/admin/conferences/blog/categories/new.html.erb +1 -0
  16. data/app/views/spina/admin/conferences/blog/posts/_form.html.erb +66 -0
  17. data/app/views/spina/admin/conferences/blog/posts/_form_post_configuration.html.erb +46 -0
  18. data/app/views/spina/admin/conferences/blog/posts/_form_post_content.html.erb +59 -0
  19. data/app/views/spina/admin/conferences/blog/posts/_form_post_seo.html.erb +7 -0
  20. data/app/views/spina/admin/conferences/blog/posts/_post.html.erb +30 -0
  21. data/app/views/spina/admin/conferences/blog/posts/edit.html.erb +1 -0
  22. data/app/views/spina/admin/conferences/blog/posts/index.html.erb +33 -0
  23. data/app/views/spina/admin/conferences/blog/posts/new.html.erb +1 -0
  24. data/app/views/spina/admin/hooks/conferences/blog/_primary_navigation.html.erb +14 -0
  25. data/config/locales/en.yml +7 -0
  26. data/config/locales/nl.yml +7 -0
  27. data/config/locales/pt-BR.yml +7 -0
  28. data/db/migrate/7_create_spina_blog_translations.rb +33 -0
  29. data/lib/spina/admin/conferences/blog/engine.rb +4 -1
  30. data/lib/spina/admin/conferences/blog/version.rb +1 -1
  31. data/lib/spina/admin/conferences/blog.rb +1 -1
  32. data/spec/controllers/spina/admin/conferences/blog/categories_controller_spec.rb +1 -0
  33. data/spec/controllers/spina/admin/conferences/blog/posts_controller_spec.rb +1 -0
  34. data/spec/dummy/app/assets/config/manifest.js +2 -1
  35. data/spec/dummy/app/assets/stylesheets/default/{application.css.sass → application.css} +0 -0
  36. data/spec/dummy/config/environments/development.rb +3 -0
  37. data/spec/dummy/config/environments/test.rb +4 -0
  38. data/spec/dummy/config/initializers/mobility.rb +1 -0
  39. data/spec/dummy/config/initializers/spina.rb +1 -1
  40. data/spec/dummy/config/storage.yml +7 -0
  41. data/spec/dummy/db/migrate/20210403091403_add_url_title_to_spina_page_translations.spina.rb +6 -0
  42. data/spec/dummy/db/migrate/20210403091404_add_json_attributes_to_spina_accounts.spina.rb +6 -0
  43. data/spec/dummy/db/migrate/20210403091405_add_json_attributes_to_spina_pages.spina.rb +6 -0
  44. data/spec/dummy/db/migrate/20210403091406_add_slug_to_spina_resources.spina.rb +6 -0
  45. data/spec/dummy/db/migrate/20210404093002_create_active_storage_tables.active_storage.rb +36 -0
  46. data/spec/dummy/db/schema.rb +61 -1
  47. data/spec/factories/spina/admin/conferences/blog/posts.rb +1 -0
  48. data/spec/factories/spina/users.rb +10 -0
  49. data/spec/support/controller_helpers.rb +4 -3
  50. data/spec/support/system_tests.rb +1 -1
  51. data/spec/system/spina/admin/conferences/blog/posts_spec.rb +5 -7
  52. metadata +143 -126
  53. data/app/views/layouts/spina/admin/conferences/blog/blog.html.haml +0 -29
  54. data/app/views/layouts/spina/admin/conferences/blog/categories.html.haml +0 -10
  55. data/app/views/spina/admin/conferences/blog/categories/_category.html.haml +0 -8
  56. data/app/views/spina/admin/conferences/blog/categories/_form.html.haml +0 -42
  57. data/app/views/spina/admin/conferences/blog/categories/edit.html.haml +0 -3
  58. data/app/views/spina/admin/conferences/blog/categories/index.html.haml +0 -23
  59. data/app/views/spina/admin/conferences/blog/categories/new.html.haml +0 -2
  60. data/app/views/spina/admin/conferences/blog/posts/_form.html.haml +0 -43
  61. data/app/views/spina/admin/conferences/blog/posts/_form_post_configuration.html.haml +0 -50
  62. data/app/views/spina/admin/conferences/blog/posts/_form_post_content.html.haml +0 -46
  63. data/app/views/spina/admin/conferences/blog/posts/_form_post_seo.html.haml +0 -14
  64. data/app/views/spina/admin/conferences/blog/posts/_post.html.haml +0 -19
  65. data/app/views/spina/admin/conferences/blog/posts/edit.html.haml +0 -3
  66. data/app/views/spina/admin/conferences/blog/posts/index.html.haml +0 -23
  67. data/app/views/spina/admin/conferences/blog/posts/new.html.haml +0 -2
  68. data/app/views/spina/admin/hooks/conferences/blog/_primary_navigation.html.haml +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5789ad976200e5c76aa6b4e8f7e541b6fb614d1a8533cde5ce8fc6b3a7c8954e
4
- data.tar.gz: 769bcbbd756cc4eb32e4f037795a450bc2230a13abbc0195b92b3036edd718e5
3
+ metadata.gz: e5026172295d936e364a86d743c2b0447f8fab1c9d5ac038407c91a3d17721ce
4
+ data.tar.gz: '058c5d37a43844e7f0f7bdcaca4b5ba00f61ee37b250ed6f90e0221dc26738fa'
5
5
  SHA512:
6
- metadata.gz: 5f65f76f537fb0e3a9e9e15a60ce41f3fe3538f0f24d582ebaefe5792851bc806938bdb0ed279a4145df72a9782bda89fd8766c189bd20831f4c6ce8d1f95bed
7
- data.tar.gz: f9021df76794a220a1fd07632d70baff977cf01b43c819086f969a3e2980bf4483cfe197f8c304eb64ffc729f5bd13ae841e0437dc0a22eafa2c8853bc2de7c6
6
+ metadata.gz: e5c4f8c1a83786d973a9b82757494e4cf83586dd184386a16d0104ce1fa1d6b3fcae0b056cf9e44b718a9e27d7ea4e72e826101895b30192b48c1bf443a8058b
7
+ data.tar.gz: adfc2cdd2be06769155f8148711827bf961e71e5525a72200466c0bb9754c45736378e63f6c06d66fed5a8c41bcb05525c2c0560d6aed952392c4edcbaf23ae4
data/README.md CHANGED
@@ -28,6 +28,17 @@ Or install it yourself as:
28
28
  $ gem install spina-blog
29
29
  ```
30
30
 
31
+ Add to your Spina theme:
32
+ ```ruby
33
+ # config/initializers/themes/theme.rb
34
+
35
+ Spina::Theme.register do |theme|
36
+ # ...
37
+ theme.plugins = ['blog']
38
+ end
39
+
40
+ ```
41
+
31
42
  ## Contributing
32
43
  The version of Spina to develop this engine against is defined in the gemspec. To override the version of refinery to develop against, edit the project Gemfile to point to a local path containing a clone of Spina CMS.
33
44
 
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin
5
+ module Conferences
6
+ module Blog
7
+ # Base class for components
8
+ class ApplicationComponent < Spina::ApplicationComponent
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,39 @@
1
+ <div class="relative" data-controller="reveal" data-reveal-away-value>
2
+ <button type="button" class="btn btn-default px-3" data-action="reveal#toggle">
3
+ <%= helpers.heroicon("chat-alt", style: :solid, class: 'w-4 h-4 text-gray-300') %>
4
+ <% if @label.present? %>
5
+ <span class="ml-1 font-semibold"><%= @label %></span>
6
+ <% elsif missing_locales.any? %>
7
+ <span class="hidden md:block ml-1"><%=t 'spina.pages.missing_translations' %></span>
8
+ <% end %>
9
+ <% if missing_locales.any? %>
10
+ <div class="bg-yellow-500 rounded text-white leading-4 ml-2 w-4 font-medium text-center text-xs">
11
+ <%= missing_locales.size %>
12
+ </div>
13
+ <% end %>
14
+ </button>
15
+
16
+ <div hidden data-reveal data-transition class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg border border-gray-200 z-20">
17
+ <div class="rounded-md bg-white shadow-xs">
18
+ <div class="py-1">
19
+ <% if missing_locales.any? %>
20
+ <% missing_locales.each do |locale| %>
21
+ <%= link_to generate_edit_link(locale), class: "block px-3 py-2 text-sm leading-4 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900 font-medium" do %>
22
+ <div class="text-yellow-500">
23
+ <%=t('spina.pages.add_translation', language: '<span class="font-semibold">' + t("spina.languages.#{locale}") + '</span>').html_safe %>
24
+ </div>
25
+ <% end %>
26
+ <% end %>
27
+ <div class="border-b border-gray-200 my-1"></div>
28
+ <% end %>
29
+ <% existing_locales.each do |locale| %>
30
+ <%= link_to generate_edit_link(locale), class: "block px-3 py-2 text-sm leading-4 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900 font-medium" do %>
31
+ <div class="text-gray-700">
32
+ <%=t('spina.pages.edit_translation', language: '<span class="font-semibold">' + t("spina.languages.#{locale}") + '</span>').html_safe %>
33
+ </div>
34
+ <% end %>
35
+ <% end %>
36
+ </div>
37
+ </div>
38
+ </div>
39
+ </div>
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin
5
+ module Conferences
6
+ module Blog
7
+ # A component for switching translations, mimicking Spina::Pages::TranslationsComponent.
8
+ # Model must be either a {Spina::Admin::Conferences::Blog::Category} or a
9
+ # {Spina::Admin::Conferences::Blog::Post}
10
+ class TranslationsComponent < ApplicationComponent
11
+ def initialize(model, label: nil)
12
+ @model = model
13
+ @label = label
14
+ end
15
+
16
+ def render?
17
+ spina_locales.many?
18
+ end
19
+
20
+ def missing_locales
21
+ spina_locales - existing_locales
22
+ end
23
+
24
+ def existing_locales
25
+ @existing_locales ||= @model.translations.pluck(:locale).map(&:to_sym).sort_by do |locale|
26
+ spina_locales.index(locale)
27
+ end
28
+ end
29
+
30
+ def generate_edit_link(locale)
31
+ return helpers.spina.edit_admin_conferences_blog_category_path(@model.id, locale: locale) if @model.is_a? Category
32
+ return helpers.spina.edit_admin_conferences_blog_post_path(@model.id, locale: locale) if @model.is_a? Post
33
+ end
34
+
35
+ private
36
+
37
+ def spina_locales
38
+ Spina.config.locales.map(&:to_sym)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -4,15 +4,15 @@ module Spina
4
4
  module Admin
5
5
  module Conferences::Blog
6
6
  # Spina::Admin::Conferences::Blog::CategoriesController
7
- class CategoriesController < AdminController
7
+ class CategoriesController < ::Spina::Admin::AdminController
8
8
  before_action :category, except: %i[new create index]
9
9
  before_action :set_breadcrumb
10
10
  before_action :set_locale
11
+
12
+ admin_section :blog
11
13
 
12
14
  decorates_assigned :category
13
15
 
14
- layout 'spina/admin/conferences/blog/categories'
15
-
16
16
  def index
17
17
  @categories = Spina::Admin::Conferences::Blog::Category.order(:name)
18
18
  end
@@ -30,7 +30,7 @@ module Spina
30
30
  notice: t('spina.blog.categories.saved')
31
31
  else
32
32
  add_breadcrumb I18n.t('spina.blog.categories.new')
33
- render :new, layout: 'spina/admin/admin'
33
+ render :new, status: :unprocessable_entity
34
34
  end
35
35
  end
36
36
 
@@ -40,13 +40,14 @@ module Spina
40
40
  end
41
41
 
42
42
  def update
43
+ add_breadcrumb @category.name
43
44
  if @category.update(category_params)
44
45
  add_breadcrumb @category.name
45
46
  redirect_to spina.edit_admin_conferences_blog_category_url(
46
47
  @category.id, params: { locale: @locale }
47
48
  ), notice: t('spina.blog.categories.saved')
48
49
  else
49
- render :edit, layout: 'spina/admin/admin'
50
+ render :edit, status: :unprocessable_entity
50
51
  end
51
52
  end
52
53
 
@@ -4,16 +4,16 @@ module Spina
4
4
  module Admin
5
5
  module Conferences::Blog
6
6
  # Spina::Admin::Conferences::Blog::PostsController
7
- class PostsController < AdminController
7
+ class PostsController < ::Spina::Admin::AdminController
8
8
  before_action :post, except: %i[index live draft future new create]
9
9
  before_action :set_breadcrumb
10
10
  before_action :set_tabs, only: %i[new create edit update]
11
11
  before_action :set_locale
12
+
13
+ admin_section :blog
12
14
 
13
15
  decorates_assigned :post
14
16
 
15
- layout 'spina/admin/conferences/blog/blog'
16
-
17
17
  def index
18
18
  @posts = Spina::Admin::Conferences::Blog::Post.order(created_at: :desc)
19
19
  end
@@ -46,7 +46,7 @@ module Spina
46
46
  notice: t('spina.blog.posts.saved')
47
47
  else
48
48
  add_breadcrumb I18n.t('spina.blog.posts.new')
49
- render :new, layout: 'spina/admin/admin'
49
+ render :new, status: :unprocessable_entity
50
50
  end
51
51
  end
52
52
 
@@ -62,7 +62,7 @@ module Spina
62
62
  @post.id, params: { locale: @locale }
63
63
  ), notice: t('spina.blog.posts.saved')
64
64
  else
65
- render :edit, layout: 'spina/admin/admin'
65
+ render :edit, status: :unprocessable_entity
66
66
  end
67
67
  end
68
68
 
@@ -8,9 +8,11 @@ module Spina
8
8
 
9
9
  friendly_id :name, use: :slugged
10
10
 
11
- has_many :posts, class_name: 'Spina::Blog::Post', inverse_of: :category
11
+ has_many :posts, class_name: 'Spina::Admin::Conferences::Blog::Post', inverse_of: :category
12
12
  validates :name, presence: true, uniqueness: { case_sensitive: false }
13
13
 
14
+ translates :name, fallbacks: true
15
+
14
16
  def to_s
15
17
  name
16
18
  end
@@ -27,6 +27,10 @@ module Spina
27
27
  scope :featured, -> { where(featured: true) }
28
28
  scope :unfeatured, -> { where(featured: false) }
29
29
 
30
+ translates :title, fallbacks: true
31
+ translates :description, :excerpt, :content
32
+ translates :seo_title, default: -> { title }
33
+
30
34
  def self.table_name
31
35
  'spina_blog_posts'
32
36
  end
@@ -0,0 +1 @@
1
+ <%= yield %>
@@ -0,0 +1,13 @@
1
+ <div class="border-b border-gray-200 flex items-center justify-between space-x-8 pr-2">
2
+ <%= link_to spina.edit_admin_conferences_blog_category_path(category.id), class: 'block text-spina text-sm p-4 hover:text-spina-dark font-medium' do %>
3
+ <%= category.name %>
4
+ <% end %>
5
+
6
+ <div class="flex-1"></div>
7
+
8
+ <div>
9
+ <%= link_to spina.edit_admin_conferences_blog_category_path(category.id), class: "btn btn-default px-3" do %>
10
+ <%= heroicon('pencil', style: :solid, class: 'w-4 h-4') %>
11
+ <% end %>
12
+ </div>
13
+ </div>
@@ -0,0 +1,38 @@
1
+ <%= render Spina::UserInterface::HeaderComponent.new do |header| %>
2
+ <% header.actions do %>
3
+
4
+ <% if @category.persisted? %>
5
+ <!-- Translations -->
6
+ <%= render Spina::Admin::Conferences::Blog::TranslationsComponent.new(@category, label: @locale.upcase) %>
7
+
8
+ <%= render Spina::UserInterface::DropdownComponent.new do |dropdown| %>
9
+ <% dropdown.button(classes: "btn btn-default px-3") do %>
10
+ <%= heroicon('dots-horizontal', style: :solid, class: "w-5 h-5 text-gray-600") %>
11
+ <% end %>
12
+
13
+ <% dropdown.menu do %>
14
+ <%= button_to t('spina.permanently_delete'), spina.admin_conferences_blog_category_path(@category.id), method: :delete, class: "block w-full text-left px-4 py-2 text-sm leading-5 font-medium text-red-500 cursor-pointer bg-white hover:bg-red-100 hover:bg-opacity-50 hover:text-red-500 focus:outline-none focus:bg-gray-100 focus:text-gray-900", form: {data: {controller: "confirm", confirm_message: t('spina.blog.categories.delete_confirmation', subject: @category.name)}} %>
15
+ <% end %>
16
+ <% end %>
17
+ <% end %>
18
+
19
+ <%= button_tag type: :submit, form: dom_id(@category), class: 'btn btn-primary', data: {controller: "button", action: "button#loading", loading_message: t('spina.ui.saving')} do %>
20
+ <%= heroicon('check', style: :solid, class: 'w-5 h-5 mr-1 -ml-2') %>
21
+ <%=t 'spina.blog.categories.save' %>
22
+ <% end %>
23
+ <% end %>
24
+ <% end %>
25
+
26
+ <div class="p-8">
27
+
28
+ <%= form_with model: @category, url: @category.new_record? ? spina.admin_conferences_blog_categories_path : spina.admin_conferences_blog_category_path(@category.id), id: dom_id(@category) do |f| %>
29
+ <%= hidden_field_tag :locale, @locale %>
30
+ <% Mobility.with_locale(@locale) do %>
31
+ <%= render Spina::Forms::GroupComponent.new(label: Spina::Admin::Conferences::Blog::Category.human_attribute_name(:name)) do %>
32
+ <%= render Spina::Forms::LabelComponent.new(f, :name) %>
33
+ <%= render Spina::Forms::TextFieldComponent.new(f, :name, autofocus: @category.name.blank?) %>
34
+ <% end %>
35
+ <% end %>
36
+ <% end %>
37
+
38
+ </div>
@@ -0,0 +1 @@
1
+ <%= render 'form' %>
@@ -0,0 +1,19 @@
1
+ <%= render Spina::UserInterface::HeaderComponent.new do |header| %>
2
+ <% header.after_breadcrumbs do %>
3
+ <%= link_to spina.new_admin_conferences_blog_category_path, class: 'btn btn-default h-8 px-2 ml-3' do %>
4
+ <%= heroicon('plus', style: :solid, class: 'w-6 h-6') %>
5
+ <% end %>
6
+ <% end %>
7
+ <% end %>
8
+
9
+ <div class="p-8">
10
+ <% if @categories.any? %>
11
+ <div class="shadow-sm border border-gray-200 bg-white rounded-lg">
12
+ <%= render partial: 'category', collection: @categories, as: :category %>
13
+ </div>
14
+ <% else %>
15
+ <div class="text-gray-700 italic">
16
+ There are no categories yet. Create your first one!
17
+ </div>
18
+ <% end %>
19
+ </div>
@@ -0,0 +1 @@
1
+ <%= render 'form' %>
@@ -0,0 +1,66 @@
1
+ <div data-controller="tabs" data-tabs-active="cursor-default text-gray-900 bg-spina-dark bg-opacity-10" data-tabs-inactive="cursor-pointer bg-transparent text-gray-400 border-transparent">
2
+ <%= render Spina::UserInterface::HeaderComponent.new do |header| %>
3
+ <% header.actions do %>
4
+
5
+ <% if @post.persisted? %>
6
+ <!-- Translations -->
7
+ <%= render Spina::Admin::Conferences::Blog::TranslationsComponent.new(@post, label: @locale.upcase) %>
8
+
9
+ <%= render Spina::UserInterface::DropdownComponent.new do |dropdown| %>
10
+ <% dropdown.button(classes: "btn btn-default px-3") do %>
11
+ <%= heroicon('dots-horizontal', style: :solid, class: "w-5 h-5 text-gray-600") %>
12
+ <% end %>
13
+
14
+ <% dropdown.menu do %>
15
+ <%= button_to t('spina.permanently_delete'), spina.admin_conferences_blog_post_path(@post.id), method: :delete, class: "block w-full text-left px-4 py-2 text-sm leading-5 font-medium text-red-500 cursor-pointer bg-white hover:bg-red-100 hover:bg-opacity-50 hover:text-red-500 focus:outline-none focus:bg-gray-100 focus:text-gray-900", form: {data: {controller: "confirm", confirm_message: t('spina.blog.posts.delete_confirmation', subject: @post.title)}} %>
16
+ <% end %>
17
+ <% end %>
18
+ <% end %>
19
+
20
+ <%= button_tag type: :submit, form: dom_id(@post), class: 'btn btn-primary', data: {controller: "button", action: "button#loading", loading_message: t('spina.ui.saving')} do %>
21
+ <%= heroicon('check', style: :solid, class: 'w-5 h-5 mr-1 -ml-2') %>
22
+ <%=t 'spina.blog.posts.save' %>
23
+ <% end %>
24
+
25
+ <% end %>
26
+
27
+ <% header.navigation do %>
28
+ <nav class="-mb-3 mt-4">
29
+ <ul class="inline-flex w-auto rounded-md bg-white">
30
+ <% @tabs.each do |tab| %>
31
+ <button type="button" class="block px-3 leading-relaxed py-1 hover:text-gray-800 rounded-md text-gray-400 font-medium text-sm flex items-center whitespace-nowrap" data-action="tabs#show" data-tabs-target="button" data-pane-id="<%= tab %>">
32
+ <%= t("spina.blog.posts.#{tab}") %>
33
+ </button>
34
+ <% end %>
35
+ </ul>
36
+ </nav>
37
+ <% end %>
38
+ <% end %>
39
+
40
+ <div class="p-8">
41
+
42
+ <%= form_with model: @post, url: @post.new_record? ? spina.admin_conferences_blog_posts_path : spina.admin_conferences_blog_post_path(@post.id), id: dom_id(@post) do |f| %>
43
+ <%= hidden_field_tag :locale, @locale %>
44
+
45
+ <% if f.object.errors %>
46
+ <ul class="text-red-500 font-medium">
47
+ <% f.object.errors.full_messages.each do |message| %>
48
+ <li><%= message %></li>
49
+ <% end %>
50
+ </ul>
51
+ <% end %>
52
+
53
+ <% Mobility.with_locale(@locale) do %>
54
+
55
+ <% @tabs.each do |tab| %>
56
+ <div data-tabs-target="pane" id="<%= tab %>">
57
+ <%= render "form_#{tab}", f: f %>
58
+ </div>
59
+ <% end %>
60
+
61
+ <% end %>
62
+
63
+ <% end %>
64
+
65
+ </div>
66
+ </div>
@@ -0,0 +1,46 @@
1
+ <%= render Spina::Forms::GroupComponent.new(label: Spina::Admin::Conferences::Blog::Category.human_attribute_name(:category)) do %>
2
+ <%= render Spina::Forms::LabelComponent.new(f, :category) %>
3
+ <%= f.select :category_id, Spina::Admin::Conferences::Blog::Category.all.collect{|u| [u.name, u.id]}, {prompt: true}, class: 'form-select', data: {controller: "select-placeholder", action: "select-placeholder#update"} %>
4
+ <% end %>
5
+
6
+ <div class="border-t border-gray-200 my-6"></div>
7
+
8
+ <%= render Spina::Forms::GroupComponent.new(label: Spina::Admin::Conferences::Blog::Category.human_attribute_name(:draft), description: Spina::Admin::Conferences::Blog::Post.human_attribute_name(:draft_description)) do %>
9
+ <%= render Spina::Forms::LabelComponent.new(f, :draft) %>
10
+ <%= render Spina::Forms::SwitchComponent.new(f, :draft) %>
11
+ <% end %>
12
+
13
+ <div class="border-t border-gray-200 my-6"></div>
14
+
15
+ <%= render Spina::Forms::GroupComponent.new(label: Spina::Admin::Conferences::Blog::Category.human_attribute_name(:featured), description: Spina::Admin::Conferences::Blog::Post.human_attribute_name(:featured_description)) do %>
16
+ <%= render Spina::Forms::LabelComponent.new(f, :featured) %>
17
+ <%= render Spina::Forms::SwitchComponent.new(f, :featured) %>
18
+ <% end %>
19
+
20
+ <div class="border-t border-gray-200 my-6"></div>
21
+
22
+ <%= render Spina::Forms::GroupComponent.new(label: Spina::Admin::Conferences::Blog::Category.human_attribute_name(:slug), description: Spina::Admin::Conferences::Blog::Category.human_attribute_name(:slug_description)) do %>
23
+ <%= render Spina::Forms::LabelComponent.new(f, :slug) %>
24
+ <%= render Spina::Forms::TextFieldComponent.new(f, :slug) %>
25
+
26
+ <% if @post.slug %>
27
+ <div class="text-gray-400 text-xs mt-2">
28
+ Current permalink:
29
+ /blog/posts/<%= @post.slug %>
30
+ </div>
31
+ <% end %>
32
+ <% end %>
33
+
34
+ <div class="border-t border-gray-200 my-6"></div>
35
+
36
+ <%= render Spina::Forms::GroupComponent.new(label: Spina::Admin::Conferences::Blog::Category.human_attribute_name(:published_at)) do %>
37
+ <%= render Spina::Forms::LabelComponent.new(f, :published_at) %>
38
+ <%= render Spina::Forms::TextFieldComponent.new(f, :published_at) %>
39
+ <% end %>
40
+
41
+ <div class="border-t border-gray-200 my-6"></div>
42
+
43
+ <%= render Spina::Forms::GroupComponent.new(label: Spina::Admin::Conferences::Blog::Category.human_attribute_name(:spina_user)) do %>
44
+ <%= render Spina::Forms::LabelComponent.new(f, :spina_user) %>
45
+ <%= f.select :user_id, Spina::User.all.collect{|u| [u.name, u.id]}, {prompt: true}, class: 'form-select', data: {controller: "select-placeholder", action: "select-placeholder#update"} %>
46
+ <% end %>
@@ -0,0 +1,59 @@
1
+ <%= render Spina::Forms::LabelComponent.new(f, :title) %>
2
+ <%= render Spina::Forms::TextFieldComponent.new(f, :title, size: "lg", autofocus: f.object.title.blank?) %>
3
+
4
+ <div class="mt-6">
5
+ <%= render Spina::Forms::LabelComponent.new(f, :excerpt) %>
6
+
7
+ <div class="mt-1 relative">
8
+ <%= f.hidden_field :excerpt, id: "excerpt_input" %>
9
+
10
+ <div class="relative form-textarea p-4 shadow-sm max-w-5xl" data-controller="trix" id="insert_excerpt_trix-toolbar" data-action="media-picker:done->trix#insertAttachment">
11
+ <%= render Spina::Forms::TrixToolbarComponent.new("excerpt_trix-toolbar") %>
12
+
13
+ <trix-editor class="prose prose-sm focus:outline-none max-w-3xl xl:border-r border-dashed border-gray-200 pr-3" toolbar="excerpt_trix-toolbar" input="excerpt_input" data-trix-target="editor" data-action="trix-file-accept->trix#preventDefault"></trix-editor>
14
+ </div>
15
+ </div>
16
+ </div>
17
+
18
+ <div class="mt-6">
19
+ <%= render Spina::Forms::LabelComponent.new(f, :content) %>
20
+ <div class="mt-1 relative">
21
+
22
+ <%= f.hidden_field :content, id: "content_input" %>
23
+
24
+ <div class="relative form-textarea p-4 shadow-sm max-w-5xl" data-controller="trix" id="insert_content_trix-toolbar" data-action="media-picker:done->trix#insertAttachment">
25
+ <%= render Spina::Forms::TrixToolbarComponent.new("content_trix-toolbar") %>
26
+
27
+ <trix-editor class="prose prose-sm focus:outline-none max-w-3xl xl:border-r border-dashed border-gray-200 pr-3" toolbar="content_trix-toolbar" input="content_input" data-trix-target="editor" data-action="trix-file-accept->trix#preventDefault"></trix-editor>
28
+ </div>
29
+ </div>
30
+ </div>
31
+
32
+ <div class="mt-6 relative" data-controller="media-picker" id="<%= dom_id(f.object, f.object.object_id) %>" data-action="media-picker:done->media-picker#handleDone">
33
+ <label class="block text-sm leading-5 font-medium text-gray-700">
34
+ <%= Spina::Admin::Conferences::Blog::Post.human_attribute_name(:image) %>
35
+ </label>
36
+
37
+ <%= f.hidden_field :image_id, data: {media_picker_target: "imageId"} %>
38
+
39
+ <button class="absolute mt-3 ml-2 z-10 cursor-pointer flex items-center h-9 px-2 rounded-md bg-gray-700 bg-opacity-50 border-gray-300 active:shadow-inner text-gray-200 hover:text-white text-sm font-medium leading-none leading-none shadow-sm" data-action="media-picker#removeImage" data-media-picker-target="clearButton" type="button">
40
+ <svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
41
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
42
+ </svg>
43
+ </button>
44
+
45
+ <%= link_to spina.admin_media_picker_path(target: dom_id(f.object, f.object.object_id)), class: "block relative mt-1 w-full", data: {turbo_frame: "modal"} do %>
46
+ <div class="w-36 h-36 bg-transparent border-2 border-dashed border-gray-300 rounded-lg flex items-center flex-col justify-center">
47
+ <svg class="mx-auto h-12 w-12 text-gray-400" stroke="currentColor" fill="none" viewBox="0 0 48 48">
48
+ <path d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
49
+ </svg>
50
+ <div class="text-gray-400 font-medium text-sm"><%=t 'spina.images.choose_image' %></div>
51
+ </div>
52
+
53
+ <div class="border absolute inset-0 w-36 h-36 bg-gray-100 rounded-lg shadow-md overflow-hidden" data-media-picker-target="thumbnail">
54
+ <% if f.object.image.present? %>
55
+ <%= image_tag thumbnail_url(f.object.image), class: 'object-contain h-36 w-full', data: {controller: "image-fade-in"} %>
56
+ <% end %>
57
+ </div>
58
+ <% end %>
59
+ </div>
@@ -0,0 +1,7 @@
1
+ <%= render Spina::Forms::GroupComponent.new(label: Spina::Admin::Conferences::Blog::Post.human_attribute_name(:seo_title), description: Spina::Admin::Conferences::Blog::Post.human_attribute_name(:description)) do %>
2
+ <%= render Spina::Forms::TextFieldComponent.new(f, :seo_title) %>
3
+
4
+ <div class="mt-5">
5
+ <%= render Spina::Forms::TextFieldComponent.new(f, :description) %>
6
+ </div>
7
+ <% end %>
@@ -0,0 +1,30 @@
1
+ <div class="border-b border-gray-200 flex items-center justify-between space-x-8 pr-2">
2
+
3
+ <%= link_to spina.edit_admin_conferences_blog_post_path(post.id), class: 'flex items-center text-spina text-sm p-4 hover:text-spina-dark font-medium' do %>
4
+ <% if post.featured? %>
5
+ <%= heroicon('star', style: :solid, class: 'w-6 h-6 text-yellow-500 mr-3') %>
6
+ <% end %>
7
+ <div>
8
+ <%= post.title %>
9
+ <div class="text-gray-400 text-xs"><%= time_ago_in_words(post.created_at) %> ago</div>
10
+ </div>
11
+ <% end %>
12
+
13
+ <div class="flex-1"></div>
14
+
15
+ <div>
16
+ <% if post.draft? %>
17
+ <span class="text-sm text-gray-400"><%=t 'spina.blog.posts.concept' %></span>
18
+ <% elsif post.published_at and post.published_at > Time.now %>
19
+ <span class="text-sm text-gray-400">
20
+ Will be published on <%= post.decorate.published_date %>
21
+ </span>
22
+ <% end %>
23
+ </div>
24
+
25
+ <div>
26
+ <%= link_to spina.edit_admin_conferences_blog_post_path(post.id), class: "btn btn-default px-3" do %>
27
+ <%= heroicon('pencil', style: :solid, class: 'w-4 h-4') %>
28
+ <% end %>
29
+ </div>
30
+ </div>
@@ -0,0 +1 @@
1
+ <%= render 'form' %>
@@ -0,0 +1,33 @@
1
+ <%= render Spina::UserInterface::HeaderComponent.new do |header| %>
2
+ <% header.after_breadcrumbs do %>
3
+ <%= link_to spina.new_admin_conferences_blog_post_path, class: 'btn btn-default h-8 px-2 ml-3' do %>
4
+ <%= heroicon('plus', style: :solid, class: 'w-6 h-6') %>
5
+ <% end %>
6
+ <% end %>
7
+
8
+ <% header.navigation do %>
9
+ <nav class="-mb-3 mt-4">
10
+ <ul class="inline-flex w-auto rounded-md bg-white">
11
+ <%= render Spina::UserInterface::TabLinkComponent.new(t('spina.blog.posts.all_posts'), spina.admin_conferences_blog_posts_path, active: action_name == 'index') %>
12
+
13
+ <%= render Spina::UserInterface::TabLinkComponent.new(t('spina.blog.posts.live_posts'), spina.live_admin_conferences_blog_posts_path, active: action_name == 'live') %>
14
+
15
+ <%= render Spina::UserInterface::TabLinkComponent.new(t('spina.blog.posts.draft_posts'), spina.draft_admin_conferences_blog_posts_path, active: action_name == 'draft') %>
16
+
17
+ <%= render Spina::UserInterface::TabLinkComponent.new(t('spina.blog.posts.scheduled_posts'), spina.future_admin_conferences_blog_posts_path, active: action_name == 'future') %>
18
+ </ul>
19
+ </nav>
20
+ <% end %>
21
+ <% end %>
22
+
23
+ <div class="p-8">
24
+ <% if @posts.any? %>
25
+ <div class="shadow-sm border border-gray-200 bg-white rounded-lg">
26
+ <%= render partial: 'post', collection: @posts, as: :post %>
27
+ </div>
28
+ <% else %>
29
+ <div class="text-gray-700 italic">
30
+ There are no posts yet. Create your first one!
31
+ </div>
32
+ <% end %>
33
+ </div>
@@ -0,0 +1 @@
1
+ <%= render 'form' %>
@@ -0,0 +1,14 @@
1
+ <%= render Spina::MainNavigation::SubNavComponent.new(:blog) do |nav| %>
2
+ <% nav.icon do %>
3
+ <%= heroicon('pencil', style: :solid, class: 'w-8 h-8 text-white md:mr-3') %>
4
+
5
+ <div class="text-white font-semibold hidden md:block transform -translate-x-2 ease-in-out duration-300 absolute md:relative opacity-0 transition-all" data-navigation-target="label">
6
+ <%=t 'spina.blog.title' %>
7
+ </div>
8
+ <% end %>
9
+
10
+ <% nav.links do %>
11
+ <%= render Spina::MainNavigation::LinkComponent.new(t('spina.blog.posts.title'), spina.admin_conferences_blog_posts_path, active: request.path.start_with?("/#{Spina.config.backend_path}/conferences/blog/posts")) %>
12
+ <%= render Spina::MainNavigation::LinkComponent.new(t('spina.blog.categories.title'), spina.admin_conferences_blog_categories_path, active: request.path.start_with?("/#{Spina.config.backend_path}/conferences/blog/categories")) %>
13
+ <% end %>
14
+ <% end %>
@@ -14,6 +14,12 @@ en:
14
14
  post_content: Content
15
15
  post_configuration: Settings
16
16
  concept: Draft
17
+ all_posts: All Posts
18
+ delete_confirmation: Are you sure you want to delete <strong>%{subject}</strong>?
19
+ draft_posts: Draft Posts
20
+ live_posts: Live Posts
21
+ post_seo: Post SEO
22
+ scheduled_posts: Scheduled Posts
17
23
 
18
24
  categories:
19
25
  title: Categories
@@ -22,6 +28,7 @@ en:
22
28
  save: Save category
23
29
  saving: Saving...
24
30
  saved: Category saved
31
+ delete_confirmation: Are you sure you want to delete <strong>%{subject}</strong>?
25
32
 
26
33
  activerecord:
27
34
  attributes:
@@ -14,6 +14,12 @@ nl:
14
14
  post_content: Inhoud
15
15
  post_configuration: Instellingen
16
16
  concept: Concept
17
+ all_posts: All Posts
18
+ delete_confirmation: Are you sure you want to delete <strong>%{subject}</strong>?
19
+ draft_posts: Draft Posts
20
+ live_posts: Live Posts
21
+ post_seo: Post SEO
22
+ scheduled_posts: Scheduled Posts
17
23
 
18
24
  categories:
19
25
  title: Categorieën
@@ -22,6 +28,7 @@ nl:
22
28
  save: Categorie opslaan
23
29
  saving: Opslaan...
24
30
  saved: Categorie opgeslagen
31
+ delete_confirmation: Are you sure you want to delete <strong>%{subject}</strong>?
25
32
 
26
33
  activerecord:
27
34
  attributes: