dokno 1.2.1 → 1.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,41 +1,33 @@
1
1
  module Dokno
2
2
  class Category < ApplicationRecord
3
- belongs_to :parent,
4
- class_name: 'Dokno::Category',
5
- primary_key: 'id',
6
- foreign_key: 'category_id',
7
- optional: true,
8
- inverse_of: :children
9
- has_many :children,
10
- class_name: 'Dokno::Category',
11
- primary_key: 'id',
12
- foreign_key: 'category_id',
13
- dependent: :nullify,
14
- inverse_of: :parent
3
+ belongs_to :parent, class_name: 'Dokno::Category', primary_key: 'id', foreign_key: 'category_id', inverse_of: :children, optional: true
4
+ has_many :children, class_name: 'Dokno::Category', primary_key: 'id', foreign_key: 'category_id', inverse_of: :parent, dependent: :nullify
15
5
 
16
6
  has_and_belongs_to_many :articles
17
7
 
8
+ before_validation :set_code
18
9
  validates :name, :code, presence: true, uniqueness: true
19
10
  validate :circular_parent_check
20
11
 
21
- before_validation :set_code
22
-
23
12
  scope :alpha_order, -> { order(:name) }
24
13
 
25
- # The display breadcrumb for the Category
26
- def breadcrumb
27
- crumbs = [name]
14
+ def breadcrumb(**args)
15
+ crumbs = [(category_link(self, args) unless args[:hide_self])]
28
16
  parent_category_id = category_id
29
17
 
30
18
  loop do
31
19
  break if parent_category_id.blank?
32
20
 
33
- parent_category = self.class.find(parent_category_id)
34
- crumbs.prepend parent_category.name
21
+ parent_category = Category.find(parent_category_id)
22
+ crumbs.prepend category_link(parent_category, args)
35
23
  parent_category_id = parent_category.category_id
36
24
  end
37
25
 
38
- crumbs.join(' > ')
26
+ crumbs.compact.join("&nbsp;&nbsp;>&nbsp;&nbsp;").html_safe
27
+ end
28
+
29
+ def category_link(category, args={})
30
+ %(<a href="#{article_index_path(category.code)}?search_term=#{CGI.escape(args[:search_term].to_s)}&order=#{CGI.escape(args[:order].to_s)}">#{category.name}</a>)
39
31
  end
40
32
 
41
33
  # All Articles in the Category, including all child Categories
@@ -45,12 +37,7 @@ module Dokno
45
37
  .joins(:categories)
46
38
  .where(dokno_categories: { id: self.class.branch(parent_category_id: id).pluck(:id) })
47
39
 
48
- records = records.updated_order if order == :updated
49
- records = records.newest_order if order == :newest
50
- records = records.view_order if order == :views
51
- records = records.alpha_order if order == :alpha
52
-
53
- records
40
+ Article.apply_sort(records, order: order)
54
41
  end
55
42
 
56
43
  def branch
@@ -59,7 +46,7 @@ module Dokno
59
46
 
60
47
  # Used to invalidate the fragment cache of the hierarchical category select options
61
48
  def self.cache_key
62
- updated_timestamps = [maximum(:updated_at), Article.maximum(:updated_at)].compact.max
49
+ [maximum(:updated_at), Article.maximum(:updated_at)].compact.max
63
50
  end
64
51
 
65
52
  # The given Category and all child Categories. Useful for filtering associated articles.
@@ -78,26 +65,14 @@ module Dokno
78
65
  categories.flatten
79
66
  end
80
67
 
81
- def self.select_option_markup(selected_category_codes: nil, exclude_category_id: nil, context_category: nil, level: 0)
68
+ def self.select_option_markup(selected_category_codes: nil, context_category: nil, level: 0)
82
69
  return '' if level.positive? && context_category.blank?
83
70
 
84
71
  options = []
85
72
  level_categories = where(category_id: context_category&.id).alpha_order
86
-
87
73
  level_categories.each do |category|
88
- options << option_markup(
89
- category: category,
90
- selected_category_codes: selected_category_codes,
91
- exclude_category_id: exclude_category_id,
92
- level: level
93
- )
94
-
95
- options << select_option_markup(
96
- selected_category_codes: selected_category_codes,
97
- exclude_category_id: exclude_category_id,
98
- context_category: category,
99
- level: (level + 1)
100
- )
74
+ options << option_markup(category: category, selected_category_codes: selected_category_codes, level: level)
75
+ options << select_option_markup(selected_category_codes: selected_category_codes, context_category: category, level: (level + 1))
101
76
  end
102
77
 
103
78
  options.join
@@ -105,9 +80,7 @@ module Dokno
105
80
 
106
81
  private
107
82
 
108
- def self.option_markup(category:, selected_category_codes:, exclude_category_id:, level: 0)
109
- return '' if category.id == exclude_category_id
110
-
83
+ def self.option_markup(category:, selected_category_codes:, level: 0)
111
84
  selected = selected_category_codes&.include?(category.code)
112
85
  article_count = category.articles_in_branch.size
113
86
  %(<option value="#{category.code}" #{'selected="selected"' if selected}>#{('&nbsp;&nbsp;' * level)}#{category.name}#{' (' + article_count.to_s + ')' if article_count.positive?}</option>)
@@ -3,8 +3,31 @@
3
3
  <section class="mb-10">
4
4
 
5
5
  <div class="text-2xl mb-5 text-gray-500"><%= article.persisted? ? 'Edit' : 'New' %> Article</div>
6
- <h1 class="text-4xl mb-5 leading-tight font-light"><%= article.title %></h1>
7
- <hr class="mb-5" />
6
+ <h1 class="text-4xl leading-tight font-light"><%= article.title %></h1>
7
+ <hr class="mt-8 mb-10" />
8
+
9
+ <% if article.up_for_review? %>
10
+ <div class="bg-gray-800 text-gray-100 rounded mb-10">
11
+ <h2 class="bg-gray-900 p-10 rounded-t text-2xl leading-tight font-light">Review for Accuracy / Relevance</h2>
12
+
13
+ <div class="flex p-10">
14
+ <div class="w-1/2 pr-5">
15
+ <div>
16
+ <span class="text-lg font-semibold"><label for="reset_review_date">Reset the next review date?</label></span>
17
+ <input onchange="setReviewForm();" type="checkbox" name="reset_review_date" id="reset_review_date" value="true" class="ml-2 text-lg" <%= "checked='checked'" if @reset_review_date %> />
18
+ </div>
19
+ <div class="text-gray-400">
20
+ <%= article.review_due_days_string %>.
21
+ Check the box above to mark this article as reviewed and reset the next review date to <span class="px-2 py-1 bg-gray-900 rounded text-white font-semibold"><%= "#{Date.today + Dokno.config.article_review_period}" %></span>
22
+ </div>
23
+ </div>
24
+ <div class="w-1/2">
25
+ <div class="text-lg font-semibold"><label for="review_notes">Reviewer Notes</label></div>
26
+ <textarea placeholder="Any review notes here will be added to the change history for this article" name="review_notes" id="review_notes" class="rounded text-xl shadow-inner bg-gray-100 p-3 mt-2 w-full text-gray-900" rows="2"><%= @review_notes %></textarea>
27
+ </div>
28
+ </div>
29
+ </div>
30
+ <% end %>
8
31
 
9
32
  <div class="mb-5">
10
33
  <div class="text-lg font-semibold"><label for="slug">Slug</label></div>
@@ -13,9 +36,9 @@
13
36
  </div>
14
37
 
15
38
  <% if Dokno::Category.exists? %>
16
- <div class="mb-5">
17
- <div class="text-lg font-semibold"><label for="category_id"><%= 'Category'.pluralize article.categories.count %></label></div>
18
- <select name="category_code[]" id="category_code" multiple="multiple" size="<%= (cat_count = Dokno::Category.count) <= 4 ? (cat_count + 1).to_s : '5' %>" class="rounded text-xl shadow-inner bg-gray-100 p-2 w-full mt-2 w-full max-w-full">
39
+ <div class="mb-5 w-2/3">
40
+ <div class="text-lg font-semibold"><label for="category_code"><%= 'Category'.pluralize article.categories.count %></label></div>
41
+ <select name="category_code[]" id="category_code" multiple="multiple" size="<%= (cat_count = Dokno::Category.count) <= 9 ? (cat_count + 1).to_s : '10' %>" class="rounded text-xl shadow-inner bg-gray-100 p-2 w-full mt-2 w-full max-w-full">
19
42
  <option></option>
20
43
  <%= Dokno::Category.select_option_markup(selected_category_codes: @category_codes).html_safe %>
21
44
  </select>
@@ -27,7 +50,7 @@
27
50
  </div>
28
51
  <% end %>
29
52
 
30
- <hr class="mb-5" />
53
+ <hr class="my-10" />
31
54
 
32
55
  <div class="mb-5">
33
56
  <div class="text-lg font-semibold"><label for="title">Title</label></div>
@@ -52,11 +75,27 @@
52
75
  <div class="text-gray-500 mt-2">Detailed documentation of the described topic. Basic HTML &amp; <a target="_blank" href="https://commonmark.org/help/" title="Markdown formatting examples">markdown</a> OK.</div>
53
76
  </div>
54
77
 
55
- <div class="mt-10 text-right">
78
+ <div class="mb-5">
79
+ <div>
80
+ <span class="text-lg font-semibold"><label for="starred">Starred article?</label></span>
81
+ <input type="hidden" name="starred" value="false" />
82
+ <input type="checkbox" name="starred" id="starred" value="true" class="ml-2 text-lg" <%= "checked='checked'" if article.starred %> />
83
+ </div>
84
+ <div class="text-gray-400">
85
+ Starred articles are always listed first when browsing.
86
+ </div>
87
+ </div>
88
+
89
+ <hr class="my-10" />
90
+
91
+ <div class="text-right">
56
92
  <span class="text-lg mr-5"><a class="no-underline" href="<%= article.persisted? ? article_path(article.slug) : root_path %>">Cancel</a></span>
57
93
  <button type="submit" class="bg-gray-700 text-gray-300 hover:bg-gray-900 hover:text-white rounded py-2 px-3 font-bold"><i data-feather="check" class="inline h-5"></i> SAVE ARTICLE</button>
58
94
  </div>
59
95
 
60
96
  </section>
61
97
 
62
- <script> elem('input#slug').focus(); </script>
98
+ <script>
99
+ elem('input#slug').focus();
100
+ setReviewForm();
101
+ </script>
@@ -2,7 +2,12 @@
2
2
 
3
3
  <% if @category.present? %>
4
4
  <div class="no-print text-gray-500 mb-5">
5
- <div>Category: <a href="<%= article_index_path(@category.code) %>?search_term=<%= CGI.escape(@search_term.to_s) %>&order=<%= @order %>"><%= @category.breadcrumb %></a></div>
5
+ <div>
6
+ <% if request.referrer&.include? article_index_path %>
7
+ <a class="fixed left-0 ml-6" title="Back" href="javascript:history.back();"><i data-feather="chevrons-left" class="text-gray-300 h-5 align-text-bottom inline"></i></a>
8
+ <% end %>
9
+ <span class="text-gray-500 mr-1">Under</span> <%= @category.breadcrumb(search_term: @search_term, order: @order) %>
10
+ </div>
6
11
  </div>
7
12
  <% end %>
8
13
 
@@ -13,7 +18,10 @@
13
18
  <section>
14
19
  <div class="flex">
15
20
  <div id="dokno-article-sidebar" class="w-2/5 pr-10">
16
- <h1 class="dokno-article-content-highlight text-4xl mb-8 leading-tight font-light"><%= @article.title %></h1>
21
+ <h1 class="dokno-article-content-highlight text-4xl mb-8 leading-tight font-light">
22
+ <%= @article.title %>
23
+ <% if @article.starred %><i title="Starred article" data-feather="star" class="inline align-middle"></i><% end %>
24
+ </h1>
17
25
 
18
26
  <div>
19
27
  <div class="mb-8 no-print">
@@ -23,7 +31,7 @@
23
31
  </div>
24
32
 
25
33
  <div class="flex">
26
- <div class="no-print w-8"><i data-feather="clock" class="inline-block h-5"></i></div>
34
+ <div class="no-print w-8"><i data-feather="clock" class="inline-block h-5 mr-2"></i></div>
27
35
  <div class="w-full">
28
36
  Last updated:<br />
29
37
  <%= time_ago_in_words @article.updated_at %> ago
@@ -34,7 +42,7 @@
34
42
  </div>
35
43
 
36
44
  <div class="mt-5 flex no-print">
37
- <div class="no-print w-8"><i data-feather="eye" class="inline-block h-5"></i></div>
45
+ <div class="no-print w-8"><i data-feather="eye" class="inline-block h-5 mr-2"></i></div>
38
46
  <div class="w-full">
39
47
  Views:<br />
40
48
  <%= number_with_delimiter(@article.views, delimiter: ',') %>
@@ -43,7 +51,7 @@
43
51
 
44
52
  <% if @article.contributors.present? %>
45
53
  <div class="mt-5 flex">
46
- <div class="no-print w-8"><i data-feather="user-check" class="inline-block h-5"></i></div>
54
+ <div class="no-print w-8"><i data-feather="user-check" class="inline-block h-5 mr-2"></i></div>
47
55
  <div class="w-full">
48
56
  Contributors:<br />
49
57
  <%= @article.contributors %>
@@ -53,7 +61,7 @@
53
61
 
54
62
  <% if @article.reading_time.present? %>
55
63
  <div class="mt-5 flex no-print">
56
- <div class="no-print w-8"><i data-feather="watch" class="inline-block h-5"></i></div>
64
+ <div class="no-print w-8"><i data-feather="watch" class="inline-block h-5 mr-2"></i></div>
57
65
  <div class="w-full">
58
66
  Reading time:<br />
59
67
  <%= @article.reading_time %>
@@ -62,7 +70,7 @@
62
70
  <% end %>
63
71
 
64
72
  <div class="mt-5 flex">
65
- <div class="no-print w-8"><i data-feather="link" class="inline-block h-5"></i></div>
73
+ <div class="no-print w-8"><i data-feather="link" class="inline-block h-5 mr-2"></i></div>
66
74
  <div class="w-full">
67
75
  Permalink:<br />
68
76
  <a class="inline-block mt-1 -ml-2 px-2 py-1 bg-blue-100 rounded" title="Copy to clipboard" href="javascript:;" onclick="copyToClipboard('<%= @article.permalink(request.base_url) %>');"><%= @article.permalink(request.base_url) %></a>
@@ -71,7 +79,7 @@
71
79
 
72
80
  <% if can_edit? %>
73
81
  <div class="mt-5 flex no-print">
74
- <div class="no-print w-8"><i data-feather="crosshair" class="inline-block h-5"></i></div>
82
+ <div class="no-print w-8"><i data-feather="crosshair" class="inline-block h-5 mr-2"></i></div>
75
83
  <div class="w-full">
76
84
  Unique slug:<br />
77
85
  <a class="inline-block mt-1 -ml-2 px-2 py-1 bg-blue-100 rounded" title="Copy to clipboard" href="javascript:;" onclick="copyToClipboard('<%= j @article.slug %>');"><%= @article.slug %></a>
@@ -90,7 +98,7 @@
90
98
 
91
99
  <% if (category_name_list = @article.category_name_list(context_category_id: @category&.id)).present? %>
92
100
  <div class="mt-5 flex no-print">
93
- <div class="no-print w-8"><i data-feather="folder" class="inline-block h-5"></i></div>
101
+ <div class="no-print w-8"><i data-feather="folder" class="inline-block h-5 mr-2"></i></div>
94
102
  <div class="w-full">
95
103
  <%= category_name_list.sub(':', ':<br />').html_safe %>
96
104
  </div>
@@ -120,14 +128,14 @@
120
128
  <div class="no-print">
121
129
  <hr class="mt-5 mb-10" />
122
130
  <div class="text-right">
123
- <button title="Edit article" class="bg-blue-900 text-gray-300 hover:text-white rounded mr-5 py-2 px-3 font-bold text-sm" onclick="location.href='<%= edit_article_path(@article.slug) %>';"><i data-feather="edit" class="inline h-5"></i> EDIT</button>
131
+ <button title="Edit article" class="bg-blue-900 text-gray-300 hover:text-white rounded mr-5 py-2 px-3 font-bold text-sm" onclick="location.href='<%= edit_article_path(@article.slug) %>';"><i data-feather="edit-2" class="inline h-5"></i> EDIT</button>
124
132
 
125
133
  <% if @article.active %>
126
- <button title="Deactivate article" id="article-deactivate-button" class="bg-yellow-700 text-gray-300 hover:text-white rounded mr-5 py-2 px-3 font-bold text-base" onclick="deactivateArticle('<%= @article.slug %>');"><i data-feather="sunset" class="inline h-5"></i> DEACTIVATE</button>
134
+ <button title="Deactivate article" id="article-deactivate-button" class="bg-yellow-700 text-gray-300 hover:text-white rounded mr-5 py-2 px-3 font-bold text-base" onclick="setArticleStatus('<%= @article.slug %>', false);"><i data-feather="file-minus" class="inline h-5"></i> DEACTIVATE</button>
127
135
  <% else %>
128
- <button title="Re-activate article" id="article-activate-button" class="bg-green-700 text-gray-300 hover:text-white rounded mr-5 py-2 px-3 font-bold text-base" onclick="activateArticle('<%= @article.slug %>');"><i data-feather="sunrise" class="inline h-5"></i> RE-ACTIVATE</button>
136
+ <button title="Re-activate article" id="article-activate-button" class="bg-green-700 text-gray-300 hover:text-white rounded mr-5 py-2 px-3 font-bold text-base" onclick="setArticleStatus('<%= @article.slug %>', true);"><i data-feather="file-plus" class="inline h-5"></i> RE-ACTIVATE</button>
129
137
  <% end %>
130
- <button title="Permanently delete article" class="bg-red-700 text-gray-300 hover:text-white rounded py-2 px-3 font-bold text-base" onclick="deleteArticle('<%= @article.id %>');"><i data-feather="trash" class="inline h-5"></i> DELETE</button>
138
+ <button title="Permanently delete article" class="bg-red-700 text-gray-300 hover:text-white rounded py-2 px-3 font-bold text-base" onclick="deleteArticle('<%= @article.id %>');"><i data-feather="slash" class="inline h-5"></i> DELETE</button>
131
139
  </div>
132
140
  </div>
133
141
  <% end %>
@@ -10,13 +10,20 @@
10
10
  </div>
11
11
 
12
12
  <% if Dokno::Category.exists? %>
13
- <div class="mb-5">
13
+ <div class="mb-5 w-1/2">
14
14
  <div class="text-lg font-semibold"><label for="parent_category_code">Parent Category</label></div>
15
- <select name="parent_category_code" id="parent_category_code" class="rounded text-xl shadow-inner bg-gray-100 p-2 mt-2 w-1/2 max-w-full">
16
- <option></option>
17
- <%= Dokno::Category.select_option_markup(selected_category_codes: [@parent_category_code], exclude_category_id: category.id).html_safe %>
15
+
16
+ <select name="parent_category_code" id="parent_category_code" size="1" class="rounded text-xl shadow-inner bg-gray-100 p-2 w-full max-w-full">
17
+ <option value="">No Parent Category</option>
18
+
19
+ <optgroup label="Categories">
20
+ <% cache Dokno::Category do %>
21
+ <%= Dokno::Category.select_option_markup.html_safe %>
22
+ <% end %>
23
+ </optgroup>
18
24
  </select>
19
- <div class="text-gray-500 mt-2 w-1/2">
25
+
26
+ <div class="text-gray-500 mt-2">
20
27
  The existing category to which this category belongs.
21
28
  <% if (article_count = category.articles.count).positive? %>
22
29
  There <%= article_count > 1 ? "are #{article_count} articles" : 'is an article' %> currently in this category.
@@ -35,4 +42,8 @@
35
42
  </div>
36
43
  </section>
37
44
 
38
- <script> elem('input#name').focus(); </script>
45
+ <script>
46
+ // Client-side select of cached select list
47
+ selectOption('parent_category_code', '<%= j @parent_category_code %>');
48
+ elem('input#name').focus();
49
+ </script>
@@ -1,10 +1,31 @@
1
+ <% if !current_page?(up_for_review_path) && (@category.blank? || @search_term.present?) %>
2
+ <div class="text-center m-auto mb-10 w-full max-w-screen-xl">
3
+ <% if @search_term.present? %>
4
+ <div class="text-gray-600 text-2xl uppercase">
5
+ <%= @total_records.positive? ? "#{@total_records} #{'article'.pluralize(@total_records)}" : 'No articles' %>
6
+ found containing the search term
7
+ <div class="text-4xl leading-tight"><span class="font-serif">&ldquo;</span> <%= @search_term %> <span class="font-serif">&rdquo;</span> </div>
8
+ </div>
9
+ <% else %>
10
+ <div class="text-gray-600 text-2xl">
11
+ Browse or search
12
+ <% if (article_count = Dokno::Article.count) > 1 %>
13
+ <%= number_with_delimiter(article_count, delimiter: ',') %> articles in
14
+ <% end %>
15
+ the
16
+ </div>
17
+ <div class="text-gray-800 text-4xl leading-tight uppercase"><%= Dokno.config.app_name %> knowledgebase</div>
18
+ <% end %>
19
+ </div>
20
+ <% end %>
21
+
1
22
  <% if @category&.parent.present? %>
2
23
  <div class="text-gray-500 mb-5">
3
- <div><%= @category.breadcrumb %></div>
24
+ <div><span class="text-gray-500 mr-1">Under</span> <%= @category.breadcrumb(search_term: @search_term, order: @order, hide_self: true) %></div>
4
25
  </div>
5
26
  <% end %>
6
27
 
7
- <% if Dokno::Category.exists? || Dokno::Article.exists? %>
28
+ <% if !current_page?(up_for_review_path) && (Dokno::Category.exists? || Dokno::Article.exists?) %>
8
29
  <%= render 'partials/category_header' %>
9
30
  <% end %>
10
31
 
@@ -44,19 +65,13 @@
44
65
  <section class="border-t border-gray-300 py-10 text-xl flex">
45
66
  <div class="w-1/3 pr-10">
46
67
  <div class="flex">
47
- <div class="no-print w-10 text-gray-300"><i data-feather="chevron-right" class="inline-block"></i></div>
68
+ <div title="<%= 'Starred article' if article.starred %>" class="no-print w-10 text-gray-300"><i data-feather="<%= article.starred ? 'star' : 'chevron-right' %>" class="inline-block"></i></div>
48
69
  <div class="w-full">
49
70
  <a class="dokno-article-title <% unless article.active %>text-gray-500 italic<% end %>" href="<%= article_path article.slug %>?search_term=<%= @search_term %>&cat_code=<%= @category&.code %>&order=<%= @order %>" title="View article"><%= article.title %></a>
50
71
  </div>
51
72
  </div>
52
73
  </div>
53
74
  <div class="dokno-article-summary w-2/3 <% unless article.active %>text-gray-500 italic<% end %>">
54
- <% unless article.active %>
55
- <div class="bg-yellow-700 p-4 mb-5 rounded text-lg border-t-4 border-yellow-900 text-white font-base not-italic">
56
- <i data-feather="alert-circle" class="inline-block"></i> This article is no longer active
57
- </div>
58
- <% end %>
59
-
60
75
  <div class="dokno-article-content-highlight mb-2"><%= article.summary.presence || 'No summary provided' %></div>
61
76
 
62
77
  <div class="text-base text-gray-500">
@@ -66,14 +81,27 @@
66
81
  <% unless @order == 'alpha' %>
67
82
  <div class="text-base">
68
83
  <% if @order == 'views' %>
69
- <div class="text-gray-500">Viewed <%= number_with_delimiter(article.views, delimiter: ',') %> <%= 'time'.pluralize(article.views) %></div>
84
+ <div class="text-gray-500">This article was viewed <%= number_with_delimiter(article.views, delimiter: ',') %> <%= 'time'.pluralize(article.views) %></div>
70
85
  <% elsif @order == 'updated' %>
71
- <div class="text-gray-500">Last updated <%= time_ago_in_words article.updated_at %> ago</div>
86
+ <div class="text-gray-500">This article was last updated <%= time_ago_in_words article.updated_at %> ago</div>
72
87
  <% elsif @order == 'newest' %>
73
- <div class="text-gray-500">Added <%= time_ago_in_words article.created_at %> ago</div>
88
+ <div class="text-gray-500">This article was added <%= time_ago_in_words article.created_at %> ago</div>
74
89
  <% end %>
75
90
  </div>
76
91
  <% end %>
92
+
93
+ <% if !article.active %>
94
+ <div class="bg-yellow-700 p-4 mt-5 rounded text-lg border-t-4 border-yellow-900 text-white font-base not-italic">
95
+ <i data-feather="info" class="inline-block mr-1"></i> This article is no longer active
96
+ </div>
97
+ <% end %>
98
+
99
+ <% if article.up_for_review? %>
100
+ <div class="bg-<%= article.review_due_days.negative? ? 'red' : 'gray' %>-800 p-4 mt-5 rounded text-lg border-t-4 border-<%= article.review_due_days.negative? ? 'red' : 'gray' %>-900 text-white font-base not-italic">
101
+ <i data-feather="bell" class="inline-block mr-1"></i> <%= article.review_due_days_string %>
102
+ </div>
103
+ <% end %>
104
+
77
105
  </div>
78
106
  </section>
79
107
  <% end %>
@@ -2,6 +2,8 @@
2
2
  <html>
3
3
  <head>
4
4
  <title><%= Dokno.config.app_name %> KNOWLEDGEBASE</title>
5
+
6
+ <meta name="viewport" content="width=device-width,initial-scale=1">
5
7
  <%= csrf_meta_tags %>
6
8
  <%= csp_meta_tag %>
7
9
 
@@ -14,6 +16,7 @@
14
16
  </head>
15
17
  <body class="bg-white font-sans font-light subpixel-antialiased text-lg">
16
18
 
19
+
17
20
  <nav id="dokno-nav-container" class="bg-blue-900 text-white py-10 px-16 text-lg">
18
21
  <div class="flex items-center m-auto w-full max-w-screen-xl">
19
22
  <div class="w-1/3">
@@ -24,12 +27,12 @@
24
27
  <div class="w-2/3 text-right">
25
28
  <% if can_edit? %>
26
29
  <% if action_name != 'new' %>
27
- <button title="Add a new article" class="bg-gray-700 text-gray-300 hover:text-white hover:bg-gray-900 rounded ml-3 py-2 px-3 font-bold text-base" onclick="location.href='<%= new_article_path %>/?category_code=<%= @category&.code %>';"><i data-feather="plus-circle" class="inline h-5"></i> ARTICLE</button>
28
- <button title="Add a new category" class="bg-gray-700 text-gray-300 hover:text-white hover:bg-gray-900 rounded ml-3 py-2 px-3 font-bold text-base" onclick="location.href='<%= new_category_path %>/?parent_category_code=<%= @category&.code %>';"><i data-feather="plus-circle" class="inline h-5"></i> CATEGORY</button>
30
+ <button title="Add a new article" class="bg-gray-700 text-gray-300 hover:text-white hover:bg-gray-900 rounded ml-3 py-2 px-3 font-bold text-base" onclick="location.href='<%= new_article_path %>/?category_code=<%= @category&.code %>';"><i data-feather="plus" class="inline h-5"></i> ARTICLE</button>
31
+ <button title="Add a new category" class="bg-gray-700 text-gray-300 hover:text-white hover:bg-gray-900 rounded ml-3 py-2 px-3 font-bold text-base" onclick="location.href='<%= new_category_path %>/?parent_category_code=<%= @category&.code %>';"><i data-feather="plus" class="inline h-5"></i> CATEGORY</button>
29
32
  <% end %>
30
33
 
31
34
  <% if @category&.persisted? && action_name != 'edit' %>
32
- <button title="Edit this category" class="bg-gray-700 text-gray-100 hover:text-white hover:bg-gray-900 rounded ml-3 py-2 px-3 font-bold text-base" onclick="location.href='<%= edit_category_path(@category) %>';"><i data-feather="edit" class="inline h-5"></i> CATEGORY</button>
35
+ <button title="Edit this category" class="bg-gray-700 text-gray-100 hover:text-white hover:bg-gray-900 rounded ml-3 py-2 px-3 font-bold text-base" onclick="location.href='<%= edit_category_path(@category) %>';"><i data-feather="edit-2" class="inline h-5"></i> CATEGORY</button>
33
36
  <% end %>
34
37
  <% end %>
35
38
 
@@ -40,40 +43,22 @@
40
43
  </div>
41
44
  </nav>
42
45
 
43
- <% flash.each do |color, msg| %>
44
- <div class="bg-<%= color %>-700 text-lg text-white font-base py-10 px-16">
45
- <i data-feather="<%= (color == 'green' ? 'check-circle' : (color == 'yellow' ? 'alert-circle' : 'x-circle')) %>" class="inline mr-2"></i>
46
- <%= msg %>
46
+
47
+ <% flash.each do |type, msg| %>
48
+ <div class="bg-<%= type %>-800 text-lg text-white font-base py-10 px-16 text-center">
49
+ <i data-feather="<%= (type == 'green' ? 'smile' : (type == 'yellow' ? 'info' : (type == 'gray' ? 'bell' : 'frown'))) %>" class="inline mr-1"></i>
50
+ <%= sanitize(msg, tags: %w[a], attributes: %w[href class]) %>
47
51
  </div>
48
52
  <% end %>
49
53
 
50
- <main class="py-10 px-16">
51
- <% if @article.blank? && (@category.blank? || @search_term.present?) %>
52
- <div class="text-center m-auto mb-10 w-full max-w-screen-xl">
53
- <% if @search_term.present? %>
54
- <div class="text-gray-600 text-2xl uppercase">
55
- <%= @total_records.positive? ? "#{@total_records} #{'article'.pluralize(@total_records)}" : 'No articles' %>
56
- found containing the search term
57
- <div class="text-4xl leading-tight"><span class="font-serif">&ldquo;</span> <%= @search_term %> <span class="font-serif">&rdquo;</span> </div>
58
- </div>
59
- <% else %>
60
- <div class="text-gray-600 text-2xl">
61
- Browse or search
62
- <% if (article_count = Dokno::Article.count) > 1 %>
63
- <%= number_with_delimiter(article_count, delimiter: ',') %> articles in
64
- <% end %>
65
- the
66
- </div>
67
- <div class="text-gray-800 text-4xl leading-tight uppercase"><%= Dokno.config.app_name %> knowledgebase</div>
68
- <% end %>
69
- </div>
70
- <% end %>
71
54
 
55
+ <main class="py-10 px-16">
72
56
  <div id="dokno-content-container" class="w-full max-w-screen-xl m-auto print-this">
73
57
  <%= yield %>
74
58
  </div>
75
59
  </main>
76
60
 
61
+
77
62
  <footer id="dokno-footer-container">
78
63
  <% if @article.present? && action_name == 'show' %>
79
64
  <div id="dokno-article-log-container" data-category-id="<%= @category&.id %>" data-article-id="<%= @article.id %>">
@@ -81,10 +66,24 @@
81
66
  </div>
82
67
  <% end %>
83
68
 
69
+ <% if @show_up_for_review && (up_for_review_count = Dokno::Article.up_for_review.count).positive? %>
70
+ <div id="dokno-articles-up-for-review-container">
71
+ <div class="py-10 px-16 bg-gray-900">
72
+ <div class="w-full max-w-screen-xl m-auto">
73
+ <div class="text-xl text-white cursor-pointer" onclick="location.href='<%= up_for_review_path %>';">
74
+ <i data-feather="bell" class="inline mr-1"></i>
75
+ There <%= "#{up_for_review_count == 1 ? 'is' : 'are'} #{up_for_review_count}" %> <%= 'article'.pluralize(up_for_review_count) %> up for accuracy / relevance review
76
+ </div>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ <% end %>
81
+
84
82
  <div class="py-10 px-16 text-gray-400 bg-blue-900">
85
83
  <div class="w-full max-w-screen-xl m-auto flex">
86
84
  <div class="w-1/2">
87
- <a target="_blank" href="https://github.com/cpayne624/dokno" title="Knowledgebase">dokno</a>
85
+ <i data-feather="github" class="inline mr-1"></i>
86
+ <a target="_blank" href="https://github.com/cpayne624/dokno" title="Dokno on GitHub">dokno</a>
88
87
  </div>
89
88
  <div class="w-1/2 text-right">
90
89
  <% if user.present? %>
@@ -103,7 +102,7 @@
103
102
  <%= javascript_include_tag 'init' %>
104
103
 
105
104
  <% if @search_term.present? %>
106
- <script>highlightTerm(['<%= j @search_term.strip %>'], 'dokno-article-content-highlight');</script>
105
+ <script> highlightTerm(['<%= j @search_term.strip %>'], 'dokno-article-content-highlight'); </script>
107
106
  <% end %>
108
107
  </body>
109
108
  </html>