dokno 1.2.1 → 1.4.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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>