Skip to content

feat: widgets #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,17 @@ The following options are supported:
- `title` (String): root section's title;
- `page` (String): a view object to render;
- `redirect` (String): alternative to _page_ option - redirects to a specific slug;
- `widgets` (Array): list of widgets (as View components) to present.

Example:

```yml
root:
title: MyAdmin
redirect: posts
widgets:
- LatestAuthorsWidget
- LatestPostsWidget
```

`helper_class` (String): class or module with helper methods, used for attributes' formatters.
Expand Down Expand Up @@ -124,6 +128,7 @@ authentication:
- `slug` (String): section reference identifier;
- `name` (String): section's title;
- `type` (String): the type of section: `content`, `page`, `resource` or `url`;
- `widgets` (Array): list of widgets (as View components) to present;
- other properties depends on the section's type.

For _content_ sections:
Expand All @@ -139,6 +144,9 @@ type: content
content: >
<h1>Test content!</h1>
<p>Some test content</p>
widgets:
- LatestAuthorsWidget
- LatestPostsWidget
```

For _url_ sections:
Expand Down Expand Up @@ -179,6 +187,7 @@ For _resource_ sections:
- `show` (Hash): detail's action options (see below);
- `collection_actions` (Array of hashes): custom collection's actions;
- `member_actions` (Array of hashes): custom details's actions;
- `widgets` (Array): list of widgets (as View components) to present;
- `only` (Array of strings): list of supported actions (ex. `index`);
- `options` (Array of strings): resource options (ex. `hidden`).

Expand Down Expand Up @@ -256,6 +265,9 @@ Example:
header: The author
link_to: authors
call: author, name
widgets:
- LatestAuthorsWidget
- LatestPostsWidget
```

### Sample
Expand All @@ -280,6 +292,9 @@ authentication:
# password: 'f1891cea80fc05e433c943254c6bdabc159577a02a7395dfebbfbc4f7661d4af56f2d372131a45936de40160007368a56ef216a30cb202c66d3145fd24380906'
root:
title: Test Admin
widgets:
- LatestAuthorsWidget
- LatestPostsWidget
# page: RootPage
helper_class: AdminHelper
page_not_found: PageNotFound
Expand Down
9 changes: 8 additions & 1 deletion lib/tiny_admin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,16 @@ def configure_from_file(file)
end
end

def route_for(section, reference: nil, action: nil, query: nil)
root_path = settings.root_path == '/' ? nil : settings.root_path
route = [root_path, section, reference, action].compact.join("/")
route << "?#{query}" if query
route[0] == '/' ? route : route.prepend('/')
end

def settings
TinyAdmin::Settings.instance
end

module_function :configure, :configure_from_file, :settings
module_function :configure, :configure_from_file, :route_for, :settings
end
43 changes: 20 additions & 23 deletions lib/tiny_admin/actions/index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,35 @@ class Index < BasicAction
attr_reader :context,
:current_page,
:fields_options,
:filters_list,
:links,
:options,
:pagination,
:pages,
:params,
:query_string,
:repository,
:sort
:repository

def call(app:, context:, options:)
@context = context
@options = options || {}
evaluate_options(options)
fields = repository.fields(options: fields_options)
filters = prepare_filters(fields, filters_list)
records, total_count = repository.list(page: current_page, limit: pagination, filters: filters, sort: sort)
filters = prepare_filters(fields)
records, count = repository.list(page: current_page, limit: pagination, filters: filters, sort: options[:sort])
attributes = {
actions: context.actions,
fields: fields,
filters: filters,
links: options[:links],
prepare_record: ->(record) { repository.index_record_attrs(record, fields: fields_options) },
records: records,
slug: context.slug,
title: repository.index_title,
widgets: options[:widgets]
}

prepare_page(Views::Actions::Index, slug: context.slug) do |page|
setup_pagination(page, TinyAdmin.settings.components[:pagination], total_count: total_count)
page.update_attributes(
actions: context.actions,
fields: fields,
filters: filters,
links: links,
prepare_record: ->(record) { repository.index_record_attrs(record, fields: fields_options) },
records: records,
slug: context.slug,
title: repository.index_title
)
prepare_page(Views::Actions::Index, slug: context.slug, attributes: attributes) do |page|
setup_pagination(page, TinyAdmin.settings.components[:pagination], total_count: count)
end
end

Expand All @@ -43,17 +44,13 @@ def evaluate_options(options)
@fields_options = attribute_options(options[:attributes])
@params = context.request.params
@repository = context.repository
@filters_list = options[:filters]
@pagination = options[:pagination] || 10
@sort = options[:sort]
@links = options[:links]

@current_page = (params['p'] || 1).to_i
@query_string = params_to_s(params.except('p'))
end

def prepare_filters(fields, filters_list)
filters = (filters_list || []).map { _1.is_a?(Hash) ? _1 : { field: _1 } }
def prepare_filters(fields)
filters = (options[:filters] || []).map { _1.is_a?(Hash) ? _1 : { field: _1 } }
filters = filters.each_with_object({}) { |filter, result| result[filter[:field]] = filter }
values = (params['q'] || {})
fields.each_with_object({}) do |(name, field), result|
Expand Down
22 changes: 11 additions & 11 deletions lib/tiny_admin/actions/show.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@ def call(app:, context:, options:)
repository = context.repository
record = repository.find(context.reference)
prepare_record = ->(record_data) { repository.show_record_attrs(record_data, fields: fields_options) }
attributes = {
actions: context.actions,
fields: repository.fields(options: fields_options),
prepare_record: prepare_record,
record: record,
reference: context.reference,
slug: context.slug,
title: repository.show_title(record),
widgets: options[:widgets]
}

prepare_page(Views::Actions::Show, slug: context.slug) do |page|
page.update_attributes(
actions: context.actions,
fields: repository.fields(options: fields_options),
prepare_record: prepare_record,
record: record,
reference: context.reference,
slug: context.slug,
title: repository.show_title(record)
)
end
prepare_page(Views::Actions::Show, slug: context.slug, attributes: attributes)
rescue Plugins::BaseRepository::RecordNotFound => _e
prepare_page(options[:record_not_found_page] || Views::Pages::RecordNotFound)
end
Expand Down
10 changes: 1 addition & 9 deletions lib/tiny_admin/basic_app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,12 @@ module TinyAdmin
class BasicApp < Roda
include Utils

class << self
def authentication_plugin
plugin = TinyAdmin.settings.authentication&.dig(:plugin)
plugin_class = plugin.is_a?(String) ? Object.const_get(plugin) : plugin
plugin_class || TinyAdmin::Plugins::NoAuth
end
end

plugin :flash
plugin :not_found
plugin :render, engine: 'html'
plugin :sessions, secret: SecureRandom.hex(64)

plugin authentication_plugin, TinyAdmin.settings.authentication
plugin TinyAdmin.settings.authentication[:plugin], TinyAdmin.settings.authentication

not_found { prepare_page(TinyAdmin.settings.page_not_found).call }
end
Expand Down
18 changes: 11 additions & 7 deletions lib/tiny_admin/router.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
# frozen_string_literal: true

require 'forwardable'

module TinyAdmin
class Router < BasicApp
extend Forwardable

def_delegator TinyAdmin, :route_for

route do |r|
TinyAdmin.settings.load_settings

Expand Down Expand Up @@ -53,17 +59,15 @@ def root_route(router)
if TinyAdmin.settings.root[:redirect]
router.redirect route_for(TinyAdmin.settings.root[:redirect])
else
page = TinyAdmin.settings.root[:page]
page_class = page.is_a?(String) ? Object.const_get(page) : page
render_page prepare_page(page_class)
page_class = to_class(TinyAdmin.settings.root[:page])
render_page prepare_page(page_class, attributes: TinyAdmin.settings.root.slice(:content, :title, :widgets))
end
end

def setup_page_route(router, slug, page_data)
router.get slug do
page = prepare_page(page_data[:class], slug: slug)
page.update_attributes(content: page_data[:content]) if page_data[:content]
render_page page
attributes = page_data.slice(:content, :title, :widgets)
render_page prepare_page(page_data[:class], slug: slug, attributes: attributes)
end
end

Expand Down Expand Up @@ -139,7 +143,7 @@ def setup_member_routes(router, slug, options:)
def setup_custom_actions(router, custom_actions, options:, repository:, slug:, reference: nil)
(custom_actions || []).each_with_object({}) do |custom_action, result|
action_slug, action = custom_action.first
action_class = action.is_a?(String) ? Object.const_get(action) : action
action_class = to_class(action)

router.get action_slug.to_s do
context = Context.new(
Expand Down
4 changes: 2 additions & 2 deletions lib/tiny_admin/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ class Settings
%i[repository] => Plugins::ActiveRecordRepository,
%i[root_path] => '/admin',
%i[root page] => Views::Pages::Root,
%i[root title] => 'TinyAdmin'
%i[root title] => 'TinyAdmin',
%i[sections] => []
}.freeze

OPTIONS = %i[
Expand Down Expand Up @@ -72,7 +73,6 @@ def load_settings
end

@store ||= TinyAdmin::Store.new(self)
self.sections ||= []
self.root_path = '/' if root_path == ''

if authentication[:plugin] <= Plugins::SimpleAuth
Expand Down
18 changes: 8 additions & 10 deletions lib/tiny_admin/store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def initialize(settings)
def prepare_sections(sections, logout:)
@navbar = sections.each_with_object({}) do |section, list|
unless section.is_a?(Hash)
section_class = section.is_a?(String) ? Object.const_get(section) : section
section_class = to_class(section)
next unless section_class.respond_to?(:to_h)

section = section_class.to_h
Expand All @@ -39,28 +39,26 @@ def prepare_sections(sections, logout:)
private

def add_content_section(slug, section)
pages[slug] = { class: settings.content_page, content: section[:content] }
{ name: section[:name], path: route_for(slug), class: settings.content_page }
pages[slug] = { class: settings.content_page, content: section[:content], widgets: section[:widgets] }
{ name: section[:name], path: TinyAdmin.route_for(slug), class: settings.content_page }
end

def add_page_section(slug, section)
page = section[:page]
page_class = page.is_a?(String) ? Object.const_get(page) : page
page_class = to_class(section[:page])
pages[slug] = { class: page_class }
{ name: section[:name], path: route_for(slug), class: page_class }
{ name: section[:name], path: TinyAdmin.route_for(slug), class: page_class }
end

def add_resource_section(slug, section)
repo = section[:repository] || settings.repository
resources[slug] = {
model: section[:model].is_a?(String) ? Object.const_get(section[:model]) : section[:model],
repository: repo.is_a?(String) ? Object.const_get(repo) : repo
model: to_class(section[:model]),
repository: to_class(section[:repository] || settings.repository)
}
resource_options = section.slice(:resource, :only, :index, :show, :collection_actions, :member_actions)
resource_options[:only] ||= %i[index show]
resources[slug].merge!(resource_options)
hidden = section[:options] && (section[:options].include?(:hidden) || section[:options].include?('hidden'))
{ name: section[:name], path: route_for(slug) } unless hidden
{ name: section[:name], path: TinyAdmin.route_for(slug) } unless hidden
end

def add_url_section(_slug, section)
Expand Down
12 changes: 6 additions & 6 deletions lib/tiny_admin/utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def params_to_s(params)
list.join('&')
end

def prepare_page(page_class, slug: nil, options: nil)
def prepare_page(page_class, slug: nil, attributes: nil, options: nil)
page_class.new.tap do |page|
page.options = options
page.head_component = TinyAdmin.settings.components[:head]&.new
Expand All @@ -26,15 +26,15 @@ def prepare_page(page_class, slug: nil, options: nil)
root_title: TinyAdmin.settings.root[:title],
items: options&.include?(:no_menu) ? [] : TinyAdmin.settings.store&.navbar
)
attrs = attributes || {}
attrs[:widgets] = attrs[:widgets].map { to_class(_1) } if attrs[:widgets]
page.update_attributes(attrs) unless attrs.empty?
yield(page) if block_given?
end
end

def route_for(section, reference: nil, action: nil, query: nil)
root_path = TinyAdmin.settings.root_path == '/' ? nil : TinyAdmin.settings.root_path
route = [root_path, section, reference, action].compact.join("/")
route << "?#{query}" if query
route[0] == '/' ? route : route.prepend('/')
def to_class(klass)
klass.is_a?(String) ? Object.const_get(klass) : klass
end

def to_label(string)
Expand Down
Loading