tiny_admin 0.2.0 → 0.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 10d229ad7389d1fe4392d6f96ec993550470ba67d1b4e140df21b9d247ae3657
4
- data.tar.gz: 3f625daa624cdff4e7fe4f13618ac753cce120995daa9140dac17e55d79ef1ba
3
+ metadata.gz: d203e48b821f195f95847a646e32787c9ffd953212326f64e65ca09d563ed55e
4
+ data.tar.gz: a133ad428942d240ec8852e4885e12a51bc6a96f9a4bcf426470180671dddf6f
5
5
  SHA512:
6
- metadata.gz: 7078779b3478f2e28f5864b43839a69ee918a452a97c0851b3be45a75802fd01600f2df6d771f093032e021f98c408b40fcaffac0b4ba5fca61e5e92693bfe86
7
- data.tar.gz: c120746d7363cbda9dd39f9de6910181645da4d63bf8325c0e4d8619b2892b008c882a32e4e8b50f63ec9fc6486837843e920f4e4c917424390346fb5af67877
6
+ metadata.gz: 8955bf003179333debcf4c43dd7091b4d81fc9c1e90a6116119dc33f51b4ab7dca394d90f6f8aa6bb0385339dc5eb07b64bd546f0a92e02497d5c000cc835769
7
+ data.tar.gz: 5cabb3dc010e73b416068cca3afda56dc07ac7b024c70d734b9409298d2082798a8e5e276d892be81e82dcbfa68bad20360f0d99e4c8e7199a7bdb5067f72124
data/README.md CHANGED
@@ -4,11 +4,11 @@
4
4
 
5
5
  A compact and composable dashboard component for Ruby.
6
6
 
7
- The main features are:
8
- - a Rack app that can be mounted in any Rack-enabled framework, it can even work standalone;
7
+ Main features:
8
+ - a Rack app that can be mounted in any Rack-enabled framework or used standalone;
9
9
  - structured with plugins also for main components that can be replaced with little effort;
10
- - routing is provided by Roda, which is small and performant;
11
- - views are Phlex components, so plain Ruby objects for views and no assets are needed.
10
+ - routing is provided by Roda which is small and performant;
11
+ - views are Phlex components, so plain Ruby objects for views, no assets are needed.
12
12
 
13
13
  Please ⭐ if you like it.
14
14
 
@@ -16,9 +16,19 @@ Please ⭐ if you like it.
16
16
 
17
17
  ## Install
18
18
 
19
- - Add to your Gemfile: `gem 'tiny_admin', '~> 0.1'`
19
+ - Add to your Gemfile: `gem 'tiny_admin', '~> 0.3'`
20
20
  - Mount the app in a route (check some examples with: Hanami, Rails, Roda and standalone in [extra](extra))
21
- - Configure the dashboard using `TinyAdmin.configure` and/or `TinyAdmin.configure_from_file` (see [configuration](#configuration) below)
21
+ + in Rails, update _config/routes.rb_: `mount TinyAdmin::Router => '/admin'`
22
+ - Configure the dashboard using `TinyAdmin.configure` and/or `TinyAdmin.configure_from_file` (see [configuration](#configuration) below):
23
+
24
+ ```rb
25
+ TinyAdmin.configure do |settings|
26
+ settings.root = {
27
+ title: 'Home',
28
+ page: Admin::PageRoot
29
+ }
30
+ end
31
+ ```
22
32
 
23
33
  ## Plugins and components
24
34
 
@@ -3,21 +3,24 @@
3
3
  module TinyAdmin
4
4
  module Actions
5
5
  class Index < BasicAction
6
- attr_reader :current_page, :fields_options, :filters_list, :pagination, :query_string, :sort
6
+ attr_reader :current_page, :fields_options, :filters_list, :pagination, :pages, :query_string, :sort
7
7
 
8
8
  def call(app:, context:, options:, actions:)
9
9
  evaluate_options(options)
10
10
  fields = repository.fields(options: fields_options)
11
11
  filters = prepare_filters(fields, filters_list)
12
12
  records, total_count = repository.list(page: current_page, limit: pagination, filters: filters, sort: sort)
13
- prepare_record = ->(record) { repository.index_record_attrs(record, fields: fields_options) }
14
- title = repository.index_title
15
- pages = (total_count / pagination) + 1
16
-
17
- prepare_page(Views::Actions::Index, context: context) do |page|
18
- page.setup_pagination(current_page: current_page, pages: pages > 1 ? pages : false)
19
- page.setup_records(records: records, fields: fields, prepare_record: prepare_record)
20
- page.update_attributes(actions: actions, filters: filters, query_string: query_string, title: title)
13
+
14
+ prepare_page(Views::Actions::Index) do |page|
15
+ setup_pagination(page, settings.components[:pagination], total_count: total_count)
16
+ page.update_attributes(
17
+ actions: actions,
18
+ fields: fields,
19
+ filters: filters,
20
+ prepare_record: ->(record) { repository.index_record_attrs(record, fields: fields_options) },
21
+ records: records,
22
+ title: repository.index_title
23
+ )
21
24
  end
22
25
  end
23
26
 
@@ -43,6 +46,14 @@ module TinyAdmin
43
46
  result[field] = { value: values[field.name], filter: filters[field.name] } if filters.key?(field.name)
44
47
  end
45
48
  end
49
+
50
+ def setup_pagination(page, pagination_component_class, total_count:)
51
+ @pages = (total_count / pagination.to_f).ceil
52
+ return if pages <= 1 || !pagination_component_class
53
+
54
+ page.pagination_component = pagination_component_class.new
55
+ page.pagination_component.update(current: current_page, pages: pages, query_string: query_string)
56
+ end
46
57
  end
47
58
  end
48
59
  end
@@ -11,7 +11,7 @@ module TinyAdmin
11
11
  prepare_record = ->(record_data) { repository.show_record_attrs(record_data, fields: fields_options) }
12
12
  fields = repository.fields(options: fields_options)
13
13
 
14
- prepare_page(Views::Actions::Show, context: context) do |page|
14
+ prepare_page(Views::Actions::Show) do |page|
15
15
  page.setup_record(record: record, fields: fields, prepare_record: prepare_record)
16
16
  page.update_attributes(actions: actions, title: repository.show_title(record))
17
17
  end
@@ -8,7 +8,7 @@ module TinyAdmin
8
8
  class << self
9
9
  def configure(app, opts = {})
10
10
  @@opts = opts || {} # rubocop:disable Style/ClassVars
11
- @@opts[:password] ||= ENV.fetch('ADMIN_PASSWORD_HASH') # NOTE: fallback value
11
+ @@opts[:password] ||= ENV.fetch('ADMIN_PASSWORD_HASH', nil) # NOTE: fallback value
12
12
 
13
13
  Warden::Strategies.add(:secret) do
14
14
  def authenticate!
@@ -54,14 +54,14 @@ module TinyAdmin
54
54
  else
55
55
  page = settings.root[:page]
56
56
  page_class = page.is_a?(String) ? Object.const_get(page) : page
57
- render_page prepare_page(page_class, context: context)
57
+ render_page prepare_page(page_class)
58
58
  end
59
59
  end
60
60
 
61
61
  def setup_page_route(router, slug, page_class)
62
62
  router.get slug do
63
63
  context.slug = slug
64
- render_page prepare_page(page_class, context: context)
64
+ render_page prepare_page(page_class)
65
65
  end
66
66
  end
67
67
 
@@ -13,10 +13,18 @@ module TinyAdmin
13
13
  list.join('&')
14
14
  end
15
15
 
16
- def prepare_page(page_class, context: nil, options: nil)
16
+ def prepare_page(page_class, options: nil)
17
17
  page_class.new.tap do |page|
18
- page.context = context
19
18
  page.options = options
19
+ page.head_component = settings.components[:head]&.new
20
+ page.flash_component = settings.components[:flash]&.new
21
+ page.navbar_component = settings.components[:navbar]&.new(
22
+ current_slug: context&.slug,
23
+ root_path: settings.root_path,
24
+ root_title: settings.root[:title],
25
+ items: options&.include?(:no_menu) ? [] : settings.navbar
26
+ )
27
+
20
28
  yield(page) if block_given?
21
29
  end
22
30
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TinyAdmin
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
@@ -4,21 +4,10 @@ module TinyAdmin
4
4
  module Views
5
5
  module Actions
6
6
  class Index < DefaultLayout
7
- attr_reader :current_page, :fields, :pages, :prepare_record, :records
8
- attr_accessor :actions, :filters, :query_string
9
-
10
- def setup_pagination(current_page:, pages:)
11
- @current_page = current_page
12
- @pages = pages
13
- end
14
-
15
- def setup_records(records:, fields:, prepare_record:)
16
- @records = records
17
- @fields = fields.each_with_object({}) { |field, result| result[field.name] = field }
18
- @prepare_record = prepare_record
19
- end
7
+ attr_accessor :actions, :fields, :filters, :pagination_component, :prepare_record, :records
20
8
 
21
9
  def template
10
+ @fields = fields.each_with_object({}) { |field, result| result[field.name] = field }
22
11
  @filters ||= {}
23
12
 
24
13
  super do
@@ -57,9 +46,7 @@ module TinyAdmin
57
46
  end
58
47
  }
59
48
 
60
- if pages
61
- render components[:pagination].new(current: current_page, pages: pages, query_string: query_string)
62
- end
49
+ render pagination_component if pagination_component
63
50
  }
64
51
  end
65
52
  end
@@ -5,10 +5,6 @@ module TinyAdmin
5
5
  class BasicLayout < Phlex::HTML
6
6
  include Utils
7
7
 
8
- def components
9
- settings.components
10
- end
11
-
12
8
  def update_attributes(attributes)
13
9
  attributes.each do |key, value|
14
10
  send("#{key}=", value)
@@ -6,14 +6,6 @@ module TinyAdmin
6
6
  class Flash < Phlex::HTML
7
7
  attr_reader :errors, :notices, :warnings
8
8
 
9
- def initialize(messages:)
10
- return unless messages
11
-
12
- @notices = messages[:notices]
13
- @warnings = messages[:warnings]
14
- @errors = messages[:errors]
15
- end
16
-
17
9
  def template
18
10
  div(class: 'flash') {
19
11
  div(class: 'notices alert alert-success', role: 'alert') { notices.join(', ') } if notices&.any?
@@ -21,6 +13,14 @@ module TinyAdmin
21
13
  div(class: 'notices alert alert-danger', role: 'alert') { errors.join(', ') } if errors&.any?
22
14
  }
23
15
  end
16
+
17
+ def update(messages:)
18
+ return unless messages
19
+
20
+ @notices = messages[:notices]
21
+ @warnings = messages[:warnings]
22
+ @errors = messages[:errors]
23
+ end
24
24
  end
25
25
  end
26
26
  end
@@ -6,12 +6,6 @@ module TinyAdmin
6
6
  class Head < Phlex::HTML
7
7
  attr_reader :extra_styles, :page_title, :style_links
8
8
 
9
- def initialize(page_title, style_links: [], extra_styles: nil)
10
- @page_title = page_title
11
- @style_links = style_links
12
- @extra_styles = extra_styles
13
- end
14
-
15
9
  def template
16
10
  head {
17
11
  meta charset: 'utf-8'
@@ -25,6 +19,12 @@ module TinyAdmin
25
19
  style { extra_styles } if extra_styles
26
20
  }
27
21
  end
22
+
23
+ def update(page_title, style_links: [], extra_styles: nil)
24
+ @page_title = page_title
25
+ @style_links = style_links
26
+ @extra_styles = extra_styles
27
+ end
28
28
  end
29
29
  end
30
30
  end
@@ -6,27 +6,48 @@ module TinyAdmin
6
6
  class Pagination < Phlex::HTML
7
7
  attr_reader :current, :pages, :query_string
8
8
 
9
- def initialize(current:, pages:, query_string:)
10
- @current = current
11
- @pages = pages
12
- @query_string = query_string
13
- end
14
-
15
9
  def template
16
10
  div(class: 'pagination-div') {
17
11
  nav('aria-label' => 'Pagination') {
18
12
  ul(class: 'pagination justify-content-center') {
19
- 1.upto(pages) do |i|
20
- li_class = (i == current ? 'page-item active' : 'page-item')
21
- li(class: li_class) {
22
- href = query_string.empty? ? "?p=#{i}" : "?#{query_string}&p=#{i}"
23
- a(class: 'page-link', href: href) { i }
24
- }
13
+ if pages <= 10
14
+ pages_range(1..pages)
15
+ elsif current <= 4 || current >= pages - 3
16
+ pages_range(1..(current <= 4 ? current + 2 : 4), with_dots: true)
17
+ pages_range((current > pages - 4 ? current - 2 : pages - 2)..pages)
18
+ else
19
+ pages_range(1..1, with_dots: true)
20
+ pages_range(current - 2..current + 2, with_dots: true)
21
+ pages_range(pages..pages)
25
22
  end
26
23
  }
27
24
  }
28
25
  }
29
26
  end
27
+
28
+ def update(current:, pages:, query_string:)
29
+ @current = current
30
+ @pages = pages
31
+ @query_string = query_string
32
+ end
33
+
34
+ private
35
+
36
+ def pages_range(range, with_dots: false)
37
+ range.each do |page|
38
+ li(class: page == current ? 'page-item active' : 'page-item') {
39
+ href = query_string.empty? ? "?p=#{page}" : "?#{query_string}&p=#{page}"
40
+ a(class: 'page-link', href: href) { page }
41
+ }
42
+ end
43
+ dots if with_dots
44
+ end
45
+
46
+ def dots
47
+ li(class: 'page-item disabled') {
48
+ a(class: 'page-link') { '...' }
49
+ }
50
+ end
30
51
  end
31
52
  end
32
53
  end
@@ -3,24 +3,22 @@
3
3
  module TinyAdmin
4
4
  module Views
5
5
  class DefaultLayout < BasicLayout
6
- attr_accessor :context, :messages, :options, :title
6
+ attr_accessor :flash_component, :head_component, :messages, :navbar_component, :options, :title
7
7
 
8
8
  def template(&block)
9
+ flash_component&.update(messages: messages)
10
+ head_component&.update(title, style_links: style_links, extra_styles: settings.extra_styles)
11
+
9
12
  doctype
10
13
  html {
11
- render components[:head].new(title, style_links: style_links, extra_styles: settings.extra_styles)
14
+ render head_component if head_component
12
15
 
13
16
  body(class: body_class) {
14
- navbar_attrs = {
15
- current_slug: context&.slug,
16
- root_path: settings.root_path,
17
- root_title: settings.root[:title],
18
- items: navbar_items
19
- }
20
- render components[:navbar].new(**navbar_attrs)
17
+ render navbar_component if navbar_component
21
18
 
22
19
  main_content {
23
- render components[:flash].new(messages: messages || {})
20
+ render flash_component if flash_component
21
+
24
22
  yield_content(&block)
25
23
  }
26
24
 
@@ -35,10 +33,6 @@ module TinyAdmin
35
33
  "module-#{self.class.to_s.split('::').last.downcase}"
36
34
  end
37
35
 
38
- def navbar_items
39
- options&.include?(:no_menu) ? [] : settings.navbar
40
- end
41
-
42
36
  def main_content
43
37
  div(class: 'container main-content py-4') do
44
38
  if options&.include?(:compact_layout)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tiny_admin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mattia Roccoberton
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-10 00:00:00.000000000 Z
11
+ date: 2023-04-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: phlex