tiny_admin 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +20 -2
- data/lib/tiny_admin/actions/index.rb +23 -23
- data/lib/tiny_admin/actions/show.rb +11 -9
- data/lib/tiny_admin/authentication.rb +4 -1
- data/lib/tiny_admin/context.rb +9 -14
- data/lib/tiny_admin/router.rb +52 -37
- data/lib/tiny_admin/section.rb +14 -0
- data/lib/tiny_admin/settings.rb +9 -62
- data/lib/tiny_admin/store.rb +67 -0
- data/lib/tiny_admin/utils.rb +8 -12
- data/lib/tiny_admin/version.rb +1 -1
- data/lib/tiny_admin/views/actions/index.rb +12 -8
- data/lib/tiny_admin/views/actions/show.rb +5 -3
- data/lib/tiny_admin/views/basic_layout.rb +1 -1
- data/lib/tiny_admin/views/basic_widget.rb +8 -0
- data/lib/tiny_admin/views/components/navbar.rb +5 -5
- data/lib/tiny_admin/views/components/widgets.rb +35 -0
- data/lib/tiny_admin/views/pages/content.rb +5 -1
- data/lib/tiny_admin/views/pages/root.rb +1 -1
- data/lib/tiny_admin.rb +9 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8059ea24a18bfb14f8e868a70203db8045099e20196f5098f1c63b121421e49
|
4
|
+
data.tar.gz: d0da80d718362c58a50e963b2e79dff550e77a763d7872a0ba96efea43f408b3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3067f890f10f10f5dc7379c7b147f01f5cb62b8d59f06ad7b4387c685cd2e32cdeb1f87fbfe36b0e2c388c854ce461f1a18b2659ee479e401ba74ed3c5559a3a
|
7
|
+
data.tar.gz: 0a4b4129b33e0dbf2ab2d01df14ead0ec3971a098dbed39a5b50c287e2a00c5c8448011de81a233c7bb1dda58bb42b648d44069684a65cdb4e42e3faf2a453bb
|
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# Tiny Admin
|
2
2
|
|
3
|
-
[![Gem Version](https://badge.fury.io/rb/tiny_admin.svg)](https://badge.fury.io/rb/tiny_admin)
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/tiny_admin.svg)](https://badge.fury.io/rb/tiny_admin)
|
4
|
+
[![Gem Downloads](https://badgen.net/rubygems/dt/tiny_admin)](https://rubygems.org/gems/tiny_admin)
|
5
|
+
[![Linters](https://github.com/blocknotes/tiny_admin/actions/workflows/linters.yml/badge.svg)](https://github.com/blocknotes/tiny_admin/actions/workflows/linters.yml)
|
6
|
+
[![Specs](https://github.com/blocknotes/tiny_admin/actions/workflows/specs.yml/badge.svg)](https://github.com/blocknotes/tiny_admin/actions/workflows/specs.yml)
|
4
7
|
|
5
8
|
A compact and composable dashboard component for Ruby.
|
6
9
|
|
@@ -16,7 +19,7 @@ Please ⭐ if you like it.
|
|
16
19
|
|
17
20
|
## Install
|
18
21
|
|
19
|
-
- Add to your Gemfile: `gem 'tiny_admin', '~> 0.
|
22
|
+
- Add to your Gemfile: `gem 'tiny_admin', '~> 0.7'`
|
20
23
|
- Mount the app in a route (check some examples with: Hanami, Rails, Roda and standalone in [extra](extra))
|
21
24
|
+ in Rails, update _config/routes.rb_: `mount TinyAdmin::Router => '/admin'`
|
22
25
|
- Configure the dashboard using `TinyAdmin.configure` and/or `TinyAdmin.configure_from_file` with a YAML config file (see [configuration](#configuration) below):
|
@@ -80,6 +83,7 @@ The following options are supported:
|
|
80
83
|
- `title` (String): root section's title;
|
81
84
|
- `page` (String): a view object to render;
|
82
85
|
- `redirect` (String): alternative to _page_ option - redirects to a specific slug;
|
86
|
+
- `widgets` (Array): list of widgets (as View components) to present.
|
83
87
|
|
84
88
|
Example:
|
85
89
|
|
@@ -87,6 +91,9 @@ Example:
|
|
87
91
|
root:
|
88
92
|
title: MyAdmin
|
89
93
|
redirect: posts
|
94
|
+
widgets:
|
95
|
+
- LatestAuthorsWidget
|
96
|
+
- LatestPostsWidget
|
90
97
|
```
|
91
98
|
|
92
99
|
`helper_class` (String): class or module with helper methods, used for attributes' formatters.
|
@@ -124,6 +131,7 @@ authentication:
|
|
124
131
|
- `slug` (String): section reference identifier;
|
125
132
|
- `name` (String): section's title;
|
126
133
|
- `type` (String): the type of section: `content`, `page`, `resource` or `url`;
|
134
|
+
- `widgets` (Array): list of widgets (as View components) to present;
|
127
135
|
- other properties depends on the section's type.
|
128
136
|
|
129
137
|
For _content_ sections:
|
@@ -139,6 +147,9 @@ type: content
|
|
139
147
|
content: >
|
140
148
|
<h1>Test content!</h1>
|
141
149
|
<p>Some test content</p>
|
150
|
+
widgets:
|
151
|
+
- LatestAuthorsWidget
|
152
|
+
- LatestPostsWidget
|
142
153
|
```
|
143
154
|
|
144
155
|
For _url_ sections:
|
@@ -179,6 +190,7 @@ For _resource_ sections:
|
|
179
190
|
- `show` (Hash): detail's action options (see below);
|
180
191
|
- `collection_actions` (Array of hashes): custom collection's actions;
|
181
192
|
- `member_actions` (Array of hashes): custom details's actions;
|
193
|
+
- `widgets` (Array): list of widgets (as View components) to present;
|
182
194
|
- `only` (Array of strings): list of supported actions (ex. `index`);
|
183
195
|
- `options` (Array of strings): resource options (ex. `hidden`).
|
184
196
|
|
@@ -256,6 +268,9 @@ Example:
|
|
256
268
|
header: The author
|
257
269
|
link_to: authors
|
258
270
|
call: author, name
|
271
|
+
widgets:
|
272
|
+
- LatestAuthorsWidget
|
273
|
+
- LatestPostsWidget
|
259
274
|
```
|
260
275
|
|
261
276
|
### Sample
|
@@ -280,6 +295,9 @@ authentication:
|
|
280
295
|
# password: 'f1891cea80fc05e433c943254c6bdabc159577a02a7395dfebbfbc4f7661d4af56f2d372131a45936de40160007368a56ef216a30cb202c66d3145fd24380906'
|
281
296
|
root:
|
282
297
|
title: Test Admin
|
298
|
+
widgets:
|
299
|
+
- LatestAuthorsWidget
|
300
|
+
- LatestPostsWidget
|
283
301
|
# page: RootPage
|
284
302
|
helper_class: AdminHelper
|
285
303
|
page_not_found: PageNotFound
|
@@ -3,34 +3,38 @@
|
|
3
3
|
module TinyAdmin
|
4
4
|
module Actions
|
5
5
|
class Index < BasicAction
|
6
|
-
attr_reader :
|
6
|
+
attr_reader :context,
|
7
|
+
:current_page,
|
7
8
|
:fields_options,
|
8
|
-
:filters_list,
|
9
9
|
:links,
|
10
|
+
:options,
|
10
11
|
:pagination,
|
11
12
|
:pages,
|
12
13
|
:params,
|
13
14
|
:query_string,
|
14
|
-
:repository
|
15
|
-
:sort
|
15
|
+
:repository
|
16
16
|
|
17
17
|
def call(app:, context:, options:)
|
18
|
+
@context = context
|
19
|
+
@options = options || {}
|
18
20
|
evaluate_options(options)
|
19
21
|
fields = repository.fields(options: fields_options)
|
20
|
-
filters = prepare_filters(fields
|
21
|
-
records,
|
22
|
+
filters = prepare_filters(fields)
|
23
|
+
records, count = repository.list(page: current_page, limit: pagination, filters: filters, sort: options[:sort])
|
24
|
+
attributes = {
|
25
|
+
actions: context.actions,
|
26
|
+
fields: fields,
|
27
|
+
filters: filters,
|
28
|
+
links: options[:links],
|
29
|
+
prepare_record: ->(record) { repository.index_record_attrs(record, fields: fields_options) },
|
30
|
+
records: records,
|
31
|
+
slug: context.slug,
|
32
|
+
title: repository.index_title,
|
33
|
+
widgets: options[:widgets]
|
34
|
+
}
|
22
35
|
|
23
|
-
prepare_page(Views::Actions::Index) do |page|
|
24
|
-
setup_pagination(page, TinyAdmin.settings.components[:pagination], total_count:
|
25
|
-
page.update_attributes(
|
26
|
-
actions: context.actions,
|
27
|
-
fields: fields,
|
28
|
-
filters: filters,
|
29
|
-
links: links,
|
30
|
-
prepare_record: ->(record) { repository.index_record_attrs(record, fields: fields_options) },
|
31
|
-
records: records,
|
32
|
-
title: repository.index_title
|
33
|
-
)
|
36
|
+
prepare_page(Views::Actions::Index, slug: context.slug, attributes: attributes) do |page|
|
37
|
+
setup_pagination(page, TinyAdmin.settings.components[:pagination], total_count: count)
|
34
38
|
end
|
35
39
|
end
|
36
40
|
|
@@ -40,17 +44,13 @@ module TinyAdmin
|
|
40
44
|
@fields_options = attribute_options(options[:attributes])
|
41
45
|
@params = context.request.params
|
42
46
|
@repository = context.repository
|
43
|
-
@filters_list = options[:filters]
|
44
47
|
@pagination = options[:pagination] || 10
|
45
|
-
@sort = options[:sort]
|
46
|
-
@links = options[:links]
|
47
|
-
|
48
48
|
@current_page = (params['p'] || 1).to_i
|
49
49
|
@query_string = params_to_s(params.except('p'))
|
50
50
|
end
|
51
51
|
|
52
|
-
def prepare_filters(fields
|
53
|
-
filters = (
|
52
|
+
def prepare_filters(fields)
|
53
|
+
filters = (options[:filters] || []).map { _1.is_a?(Hash) ? _1 : { field: _1 } }
|
54
54
|
filters = filters.each_with_object({}) { |filter, result| result[filter[:field]] = filter }
|
55
55
|
values = (params['q'] || {})
|
56
56
|
fields.each_with_object({}) do |(name, field), result|
|
@@ -8,16 +8,18 @@ module TinyAdmin
|
|
8
8
|
repository = context.repository
|
9
9
|
record = repository.find(context.reference)
|
10
10
|
prepare_record = ->(record_data) { repository.show_record_attrs(record_data, fields: fields_options) }
|
11
|
+
attributes = {
|
12
|
+
actions: context.actions,
|
13
|
+
fields: repository.fields(options: fields_options),
|
14
|
+
prepare_record: prepare_record,
|
15
|
+
record: record,
|
16
|
+
reference: context.reference,
|
17
|
+
slug: context.slug,
|
18
|
+
title: repository.show_title(record),
|
19
|
+
widgets: options[:widgets]
|
20
|
+
}
|
11
21
|
|
12
|
-
prepare_page(Views::Actions::Show
|
13
|
-
page.update_attributes(
|
14
|
-
actions: context.actions,
|
15
|
-
fields: repository.fields(options: fields_options),
|
16
|
-
prepare_record: prepare_record,
|
17
|
-
record: record,
|
18
|
-
title: repository.show_title(record)
|
19
|
-
)
|
20
|
-
end
|
22
|
+
prepare_page(Views::Actions::Show, slug: context.slug, attributes: attributes)
|
21
23
|
rescue Plugins::BaseRepository::RecordNotFound => _e
|
22
24
|
prepare_page(options[:record_not_found_page] || Views::Pages::RecordNotFound)
|
23
25
|
end
|
@@ -24,7 +24,10 @@ module TinyAdmin
|
|
24
24
|
private
|
25
25
|
|
26
26
|
def render_login(notices: nil, warnings: nil, errors: nil)
|
27
|
-
|
27
|
+
login = TinyAdmin.settings.authentication[:login]
|
28
|
+
return unless login
|
29
|
+
|
30
|
+
page = prepare_page(login, options: %i[no_menu compact_layout])
|
28
31
|
page.messages = {
|
29
32
|
notices: notices || flash['notices'],
|
30
33
|
warnings: warnings || flash['warnings'],
|
data/lib/tiny_admin/context.rb
CHANGED
@@ -1,18 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module TinyAdmin
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
:resources,
|
14
|
-
:router,
|
15
|
-
:settings,
|
16
|
-
:slug
|
17
|
-
end
|
4
|
+
Context = Struct.new(
|
5
|
+
:actions,
|
6
|
+
:reference,
|
7
|
+
:repository,
|
8
|
+
:request,
|
9
|
+
:router,
|
10
|
+
:slug,
|
11
|
+
keyword_init: true
|
12
|
+
)
|
18
13
|
end
|
data/lib/tiny_admin/router.rb
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
|
3
3
|
module TinyAdmin
|
4
4
|
class Router < BasicApp
|
5
|
-
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def_delegator TinyAdmin, :route_for
|
6
8
|
|
7
9
|
route do |r|
|
8
|
-
|
9
|
-
context.settings = TinyAdmin.settings
|
10
|
+
TinyAdmin.settings.load_settings
|
10
11
|
|
11
12
|
r.on 'auth' do
|
12
|
-
context.slug = nil
|
13
13
|
r.run Authentication
|
14
14
|
end
|
15
15
|
|
@@ -26,15 +26,14 @@ module TinyAdmin
|
|
26
26
|
end
|
27
27
|
|
28
28
|
r.post '' do
|
29
|
-
context.slug = nil
|
30
29
|
r.redirect TinyAdmin.settings.root_path
|
31
30
|
end
|
32
31
|
|
33
|
-
|
32
|
+
store.pages.each do |slug, page_data|
|
34
33
|
setup_page_route(r, slug, page_data)
|
35
34
|
end
|
36
35
|
|
37
|
-
|
36
|
+
store.resources.each do |slug, options|
|
38
37
|
setup_resource_routes(r, slug, options: options || {})
|
39
38
|
end
|
40
39
|
|
@@ -43,6 +42,10 @@ module TinyAdmin
|
|
43
42
|
|
44
43
|
private
|
45
44
|
|
45
|
+
def store
|
46
|
+
@store ||= TinyAdmin.settings.store
|
47
|
+
end
|
48
|
+
|
46
49
|
def render_page(page)
|
47
50
|
if page.respond_to?(:messages=)
|
48
51
|
page.messages = { notices: flash['notices'], warnings: flash['warnings'], errors: flash['errors'] }
|
@@ -51,76 +54,83 @@ module TinyAdmin
|
|
51
54
|
end
|
52
55
|
|
53
56
|
def root_route(router)
|
54
|
-
context.slug = nil
|
55
57
|
if TinyAdmin.settings.root[:redirect]
|
56
58
|
router.redirect route_for(TinyAdmin.settings.root[:redirect])
|
57
59
|
else
|
58
|
-
|
59
|
-
|
60
|
-
render_page prepare_page(page_class)
|
60
|
+
page_class = to_class(TinyAdmin.settings.root[:page])
|
61
|
+
render_page prepare_page(page_class, attributes: TinyAdmin.settings.root.slice(:content, :title, :widgets))
|
61
62
|
end
|
62
63
|
end
|
63
64
|
|
64
65
|
def setup_page_route(router, slug, page_data)
|
65
66
|
router.get slug do
|
66
|
-
|
67
|
-
|
68
|
-
page.update_attributes(content: page_data[:content]) if page_data[:content]
|
69
|
-
render_page page
|
67
|
+
attributes = page_data.slice(:content, :title, :widgets)
|
68
|
+
render_page prepare_page(page_data[:class], slug: slug, attributes: attributes)
|
70
69
|
end
|
71
70
|
end
|
72
71
|
|
73
72
|
def setup_resource_routes(router, slug, options:)
|
74
73
|
router.on slug do
|
75
|
-
|
76
|
-
|
77
|
-
setup_member_routes(router, options: options)
|
74
|
+
setup_collection_routes(router, slug, options: options)
|
75
|
+
setup_member_routes(router, slug, options: options)
|
78
76
|
end
|
79
77
|
end
|
80
78
|
|
81
|
-
def setup_collection_routes(router, options:)
|
82
|
-
|
79
|
+
def setup_collection_routes(router, slug, options:)
|
80
|
+
repository = options[:repository].new(options[:model])
|
83
81
|
action_options = options[:index] || {}
|
84
82
|
|
85
83
|
# Custom actions
|
86
84
|
custom_actions = setup_custom_actions(
|
87
85
|
router,
|
88
86
|
options[:collection_actions],
|
89
|
-
|
90
|
-
|
87
|
+
options: action_options,
|
88
|
+
repository: repository,
|
89
|
+
slug: slug
|
91
90
|
)
|
92
91
|
|
93
92
|
# Index
|
94
93
|
if options[:only].include?(:index) || options[:only].include?('index')
|
95
94
|
router.is do
|
96
|
-
context
|
97
|
-
|
95
|
+
context = Context.new(
|
96
|
+
actions: custom_actions,
|
97
|
+
repository: repository,
|
98
|
+
request: request,
|
99
|
+
router: router,
|
100
|
+
slug: slug
|
101
|
+
)
|
98
102
|
index_action = TinyAdmin::Actions::Index.new
|
99
103
|
render_page index_action.call(app: self, context: context, options: action_options)
|
100
104
|
end
|
101
105
|
end
|
102
106
|
end
|
103
107
|
|
104
|
-
def setup_member_routes(router, options:)
|
105
|
-
|
108
|
+
def setup_member_routes(router, slug, options:)
|
109
|
+
repository = options[:repository].new(options[:model])
|
106
110
|
action_options = (options[:show] || {}).merge(record_not_found_page: TinyAdmin.settings.record_not_found)
|
107
111
|
|
108
112
|
router.on String do |reference|
|
109
|
-
context.reference = reference
|
110
|
-
|
111
113
|
# Custom actions
|
112
114
|
custom_actions = setup_custom_actions(
|
113
115
|
router,
|
114
116
|
options[:member_actions],
|
115
|
-
|
116
|
-
|
117
|
+
options: action_options,
|
118
|
+
repository: repository,
|
119
|
+
slug: slug,
|
120
|
+
reference: reference
|
117
121
|
)
|
118
122
|
|
119
123
|
# Show
|
120
124
|
if options[:only].include?(:show) || options[:only].include?('show')
|
121
125
|
router.is do
|
122
|
-
context
|
123
|
-
|
126
|
+
context = Context.new(
|
127
|
+
actions: custom_actions,
|
128
|
+
reference: reference,
|
129
|
+
repository: repository,
|
130
|
+
request: request,
|
131
|
+
router: router,
|
132
|
+
slug: slug
|
133
|
+
)
|
124
134
|
show_action = TinyAdmin::Actions::Show.new
|
125
135
|
render_page show_action.call(app: self, context: context, options: action_options)
|
126
136
|
end
|
@@ -128,15 +138,20 @@ module TinyAdmin
|
|
128
138
|
end
|
129
139
|
end
|
130
140
|
|
131
|
-
def setup_custom_actions(router, custom_actions, repository:,
|
132
|
-
context.repository = repository
|
141
|
+
def setup_custom_actions(router, custom_actions, options:, repository:, slug:, reference: nil)
|
133
142
|
(custom_actions || []).each_with_object({}) do |custom_action, result|
|
134
143
|
action_slug, action = custom_action.first
|
135
|
-
action_class =
|
144
|
+
action_class = to_class(action)
|
136
145
|
|
137
146
|
router.get action_slug.to_s do
|
138
|
-
context
|
139
|
-
|
147
|
+
context = Context.new(
|
148
|
+
actions: {},
|
149
|
+
reference: reference,
|
150
|
+
repository: repository,
|
151
|
+
request: request,
|
152
|
+
router: router,
|
153
|
+
slug: slug
|
154
|
+
)
|
140
155
|
custom_action = action_class.new
|
141
156
|
render_page custom_action.call(app: self, context: context, options: options)
|
142
157
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TinyAdmin
|
4
|
+
class Section
|
5
|
+
attr_reader :name, :options, :path, :slug
|
6
|
+
|
7
|
+
def initialize(name:, slug: nil, path: nil, options: {})
|
8
|
+
@name = name
|
9
|
+
@options = options
|
10
|
+
@path = path || TinyAdmin.route_for(slug)
|
11
|
+
@slug = slug
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/tiny_admin/settings.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
module TinyAdmin
|
4
4
|
class Settings
|
5
5
|
include Singleton
|
6
|
-
include Utils
|
7
6
|
|
8
7
|
DEFAULTS = {
|
9
8
|
%i[authentication plugin] => Plugins::NoAuth,
|
@@ -19,7 +18,8 @@ module TinyAdmin
|
|
19
18
|
%i[repository] => Plugins::ActiveRecordRepository,
|
20
19
|
%i[root_path] => '/admin',
|
21
20
|
%i[root page] => Views::Pages::Root,
|
22
|
-
%i[root title] => 'TinyAdmin'
|
21
|
+
%i[root title] => 'TinyAdmin',
|
22
|
+
%i[sections] => []
|
23
23
|
}.freeze
|
24
24
|
|
25
25
|
OPTIONS = %i[
|
@@ -38,6 +38,8 @@ module TinyAdmin
|
|
38
38
|
style_links
|
39
39
|
].freeze
|
40
40
|
|
41
|
+
attr_reader :store
|
42
|
+
|
41
43
|
OPTIONS.each do |option|
|
42
44
|
define_method(option) do
|
43
45
|
self[option]
|
@@ -70,15 +72,14 @@ module TinyAdmin
|
|
70
72
|
end
|
71
73
|
end
|
72
74
|
|
73
|
-
|
74
|
-
context.resources ||= {}
|
75
|
-
self.sections ||= []
|
75
|
+
@store ||= TinyAdmin::Store.new(self)
|
76
76
|
self.root_path = '/' if root_path == ''
|
77
77
|
|
78
78
|
if authentication[:plugin] <= Plugins::SimpleAuth
|
79
|
-
|
79
|
+
logout_path = "#{root_path}/auth/logout"
|
80
|
+
authentication[:logout] ||= TinyAdmin::Section.new(name: 'logout', slug: 'logout', path: logout_path)
|
80
81
|
end
|
81
|
-
|
82
|
+
store.prepare_sections(sections, logout: authentication[:logout])
|
82
83
|
end
|
83
84
|
|
84
85
|
def reset!
|
@@ -97,7 +98,7 @@ module TinyAdmin
|
|
97
98
|
if value.is_a?(Hash)
|
98
99
|
value.each_key do |key2|
|
99
100
|
path = [key, key2]
|
100
|
-
if DEFAULTS[path].is_a?(Class) || DEFAULTS[path].is_a?(Module)
|
101
|
+
if (DEFAULTS[path].is_a?(Class) || DEFAULTS[path].is_a?(Module)) && self[key][key2].is_a?(String)
|
101
102
|
self[key][key2] = Object.const_get(self[key][key2])
|
102
103
|
end
|
103
104
|
end
|
@@ -105,59 +106,5 @@ module TinyAdmin
|
|
105
106
|
self[key] = Object.const_get(self[key])
|
106
107
|
end
|
107
108
|
end
|
108
|
-
|
109
|
-
def prepare_navbar(sections, logout:)
|
110
|
-
items = sections.each_with_object({}) do |section, list|
|
111
|
-
unless section.is_a?(Hash)
|
112
|
-
section_class = section.is_a?(String) ? Object.const_get(section) : section
|
113
|
-
next unless section_class.respond_to?(:to_h)
|
114
|
-
|
115
|
-
section = section_class.to_h
|
116
|
-
end
|
117
|
-
|
118
|
-
slug = section[:slug].to_s
|
119
|
-
case section[:type]&.to_sym
|
120
|
-
when :content
|
121
|
-
list[slug] = add_content_section(slug, section)
|
122
|
-
when :page
|
123
|
-
list[slug] = add_page_section(slug, section)
|
124
|
-
when :resource
|
125
|
-
list[slug] = add_resource_section(slug, section)
|
126
|
-
when :url
|
127
|
-
list[slug] = add_url_section(slug, section)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
items['auth/logout'] = logout if logout
|
131
|
-
items
|
132
|
-
end
|
133
|
-
|
134
|
-
def add_content_section(slug, section)
|
135
|
-
context.pages[slug] = { class: content_page, content: section[:content] }
|
136
|
-
{ name: section[:name], path: route_for(slug), class: content_page }
|
137
|
-
end
|
138
|
-
|
139
|
-
def add_page_section(slug, section)
|
140
|
-
page = section[:page]
|
141
|
-
page_class = page.is_a?(String) ? Object.const_get(page) : page
|
142
|
-
context.pages[slug] = { class: page_class }
|
143
|
-
{ name: section[:name], path: route_for(slug), class: page_class }
|
144
|
-
end
|
145
|
-
|
146
|
-
def add_resource_section(slug, section)
|
147
|
-
repo = section[:repository] || repository
|
148
|
-
context.resources[slug] = {
|
149
|
-
model: section[:model].is_a?(String) ? Object.const_get(section[:model]) : section[:model],
|
150
|
-
repository: repo.is_a?(String) ? Object.const_get(repo) : repo
|
151
|
-
}
|
152
|
-
resource_options = section.slice(:resource, :only, :index, :show, :collection_actions, :member_actions)
|
153
|
-
resource_options[:only] ||= %i[index show]
|
154
|
-
context.resources[slug].merge!(resource_options)
|
155
|
-
hidden = section[:options] && (section[:options].include?(:hidden) || section[:options].include?('hidden'))
|
156
|
-
{ name: section[:name], path: route_for(slug) } unless hidden
|
157
|
-
end
|
158
|
-
|
159
|
-
def add_url_section(_slug, section)
|
160
|
-
section.slice(:name, :options).tap { _1[:path] = section[:url] }
|
161
|
-
end
|
162
109
|
end
|
163
110
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TinyAdmin
|
4
|
+
class Store
|
5
|
+
include Utils
|
6
|
+
|
7
|
+
attr_reader :navbar, :pages, :resources, :settings
|
8
|
+
|
9
|
+
def initialize(settings)
|
10
|
+
@pages = {}
|
11
|
+
@resources = {}
|
12
|
+
@settings = settings
|
13
|
+
end
|
14
|
+
|
15
|
+
def prepare_sections(sections, logout:)
|
16
|
+
@navbar = sections.each_with_object([]) do |section, list|
|
17
|
+
unless section.is_a?(Hash)
|
18
|
+
section_class = to_class(section)
|
19
|
+
next unless section_class.respond_to?(:to_h)
|
20
|
+
|
21
|
+
section = section_class.to_h
|
22
|
+
end
|
23
|
+
|
24
|
+
slug = section[:slug].to_s
|
25
|
+
case section[:type]&.to_sym
|
26
|
+
when :content
|
27
|
+
list << add_content_section(slug, section)
|
28
|
+
when :page
|
29
|
+
list << add_page_section(slug, section)
|
30
|
+
when :resource
|
31
|
+
list << add_resource_section(slug, section)
|
32
|
+
when :url
|
33
|
+
list << add_url_section(slug, section)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
navbar << logout if logout
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def add_content_section(slug, section)
|
42
|
+
pages[slug] = { class: settings.content_page, content: section[:content], widgets: section[:widgets] }
|
43
|
+
TinyAdmin::Section.new(name: section[:name], slug: slug)
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_page_section(slug, section)
|
47
|
+
pages[slug] = { class: to_class(section[:page]) }
|
48
|
+
TinyAdmin::Section.new(name: section[:name], slug: slug)
|
49
|
+
end
|
50
|
+
|
51
|
+
def add_resource_section(slug, section)
|
52
|
+
resource = section.slice(:resource, :only, :index, :show, :collection_actions, :member_actions)
|
53
|
+
resource[:only] ||= %i[index show]
|
54
|
+
resources[slug] = resource.merge(
|
55
|
+
model: to_class(section[:model]),
|
56
|
+
repository: to_class(section[:repository] || settings.repository)
|
57
|
+
)
|
58
|
+
|
59
|
+
hidden = section[:options] && (section[:options].include?(:hidden) || section[:options].include?('hidden'))
|
60
|
+
TinyAdmin::Section.new(name: section[:name], slug: slug) unless hidden
|
61
|
+
end
|
62
|
+
|
63
|
+
def add_url_section(slug, section)
|
64
|
+
TinyAdmin::Section.new(name: section[:name], options: section[:options], path: section[:url], slug: slug)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/tiny_admin/utils.rb
CHANGED
@@ -14,27 +14,27 @@ module TinyAdmin
|
|
14
14
|
list.join('&')
|
15
15
|
end
|
16
16
|
|
17
|
-
def prepare_page(page_class, options: nil)
|
17
|
+
def prepare_page(page_class, slug: nil, attributes: nil, options: nil)
|
18
18
|
page_class.new.tap do |page|
|
19
19
|
page.options = options
|
20
20
|
page.head_component = TinyAdmin.settings.components[:head]&.new
|
21
21
|
page.flash_component = TinyAdmin.settings.components[:flash]&.new
|
22
22
|
page.navbar_component = TinyAdmin.settings.components[:navbar]&.new
|
23
23
|
page.navbar_component&.update_attributes(
|
24
|
-
current_slug:
|
24
|
+
current_slug: slug,
|
25
25
|
root_path: TinyAdmin.settings.root_path,
|
26
26
|
root_title: TinyAdmin.settings.root[:title],
|
27
|
-
items: options&.include?(:no_menu) ? [] :
|
27
|
+
items: options&.include?(:no_menu) ? [] : TinyAdmin.settings.store&.navbar
|
28
28
|
)
|
29
|
+
attrs = attributes || {}
|
30
|
+
attrs[:widgets] = attrs[:widgets].map { to_class(_1) } if attrs[:widgets]
|
31
|
+
page.update_attributes(attrs) unless attrs.empty?
|
29
32
|
yield(page) if block_given?
|
30
33
|
end
|
31
34
|
end
|
32
35
|
|
33
|
-
def
|
34
|
-
|
35
|
-
route = [root_path, section, reference, action].compact.join("/")
|
36
|
-
route << "?#{query}" if query
|
37
|
-
route[0] == '/' ? route : route.prepend('/')
|
36
|
+
def to_class(klass)
|
37
|
+
klass.is_a?(String) ? Object.const_get(klass) : klass
|
38
38
|
end
|
39
39
|
|
40
40
|
def to_label(string)
|
@@ -42,9 +42,5 @@ module TinyAdmin
|
|
42
42
|
|
43
43
|
string.respond_to?(:humanize) ? string.humanize : string.tr('_', ' ').capitalize
|
44
44
|
end
|
45
|
-
|
46
|
-
def context
|
47
|
-
TinyAdmin::Context.instance
|
48
|
-
end
|
49
45
|
end
|
50
46
|
end
|
data/lib/tiny_admin/version.rb
CHANGED
@@ -4,14 +4,16 @@ module TinyAdmin
|
|
4
4
|
module Views
|
5
5
|
module Actions
|
6
6
|
class Index < DefaultLayout
|
7
|
-
attr_accessor :actions, :fields, :filters, :links, :pagination_component, :prepare_record, :records
|
7
|
+
attr_accessor :actions, :fields, :filters, :links, :pagination_component, :prepare_record, :records, :slug
|
8
8
|
|
9
9
|
def template
|
10
10
|
super do
|
11
11
|
div(class: 'index') {
|
12
12
|
div(class: 'row') {
|
13
13
|
div(class: 'col-4') {
|
14
|
-
h1(class: 'title') {
|
14
|
+
h1(class: 'title') {
|
15
|
+
title
|
16
|
+
}
|
15
17
|
}
|
16
18
|
div(class: 'col-8') {
|
17
19
|
actions_buttons
|
@@ -31,13 +33,15 @@ module TinyAdmin
|
|
31
33
|
if filters&.any?
|
32
34
|
div(class: 'col-3') {
|
33
35
|
filters_form = TinyAdmin::Views::Components::FiltersForm.new
|
34
|
-
filters_form.update_attributes(section_path: route_for(
|
36
|
+
filters_form.update_attributes(section_path: TinyAdmin.route_for(slug), filters: filters)
|
35
37
|
render filters_form
|
36
38
|
}
|
37
39
|
end
|
38
40
|
}
|
39
41
|
|
40
42
|
render pagination_component if pagination_component
|
43
|
+
|
44
|
+
render TinyAdmin::Views::Components::Widgets.new(widgets)
|
41
45
|
}
|
42
46
|
end
|
43
47
|
end
|
@@ -66,7 +70,7 @@ module TinyAdmin
|
|
66
70
|
field = fields[key]
|
67
71
|
td(class: "field-value-#{field.name} field-value-type-#{field.type}") {
|
68
72
|
if field.options && field.options[:link_to]
|
69
|
-
a(href: route_for(field.options[:link_to], reference: value)) {
|
73
|
+
a(href: TinyAdmin.route_for(field.options[:link_to], reference: value)) {
|
70
74
|
field.apply_call_option(record) || value
|
71
75
|
}
|
72
76
|
else
|
@@ -82,15 +86,15 @@ module TinyAdmin
|
|
82
86
|
links.each do |link|
|
83
87
|
whitespace
|
84
88
|
if link == 'show'
|
85
|
-
a(href: route_for(
|
89
|
+
a(href: TinyAdmin.route_for(slug, reference: record.id), class: link_class) { 'show' }
|
86
90
|
else
|
87
|
-
a(href: route_for(
|
91
|
+
a(href: TinyAdmin.route_for(slug, reference: record.id, action: link), class: link_class) {
|
88
92
|
to_label(link)
|
89
93
|
}
|
90
94
|
end
|
91
95
|
end
|
92
96
|
else
|
93
|
-
a(href: route_for(
|
97
|
+
a(href: TinyAdmin.route_for(slug, reference: record.id), class: link_class) { 'show' }
|
94
98
|
end
|
95
99
|
}
|
96
100
|
}
|
@@ -103,7 +107,7 @@ module TinyAdmin
|
|
103
107
|
ul(class: 'nav justify-content-end') {
|
104
108
|
(actions || {}).each do |action, action_class|
|
105
109
|
li(class: 'nav-item mx-1') {
|
106
|
-
href = route_for(
|
110
|
+
href = TinyAdmin.route_for(slug, action: action)
|
107
111
|
a(href: href, class: 'nav-link btn btn-outline-secondary') {
|
108
112
|
action_class.respond_to?(:title) ? action_class.title : action
|
109
113
|
}
|
@@ -4,7 +4,7 @@ module TinyAdmin
|
|
4
4
|
module Views
|
5
5
|
module Actions
|
6
6
|
class Show < DefaultLayout
|
7
|
-
attr_accessor :actions, :fields, :prepare_record, :record
|
7
|
+
attr_accessor :actions, :fields, :prepare_record, :record, :reference, :slug
|
8
8
|
|
9
9
|
def template
|
10
10
|
super do
|
@@ -26,7 +26,7 @@ module TinyAdmin
|
|
26
26
|
end
|
27
27
|
div(class: 'field-value col-10') {
|
28
28
|
if field.options[:link_to]
|
29
|
-
a(href: route_for(field.options[:link_to], reference: value)) {
|
29
|
+
a(href: TinyAdmin.route_for(field.options[:link_to], reference: value)) {
|
30
30
|
field.apply_call_option(record) || value
|
31
31
|
}
|
32
32
|
else
|
@@ -35,6 +35,8 @@ module TinyAdmin
|
|
35
35
|
}
|
36
36
|
}
|
37
37
|
end
|
38
|
+
|
39
|
+
render TinyAdmin::Views::Components::Widgets.new(widgets)
|
38
40
|
}
|
39
41
|
end
|
40
42
|
end
|
@@ -45,7 +47,7 @@ module TinyAdmin
|
|
45
47
|
ul(class: 'nav justify-content-end') {
|
46
48
|
(actions || {}).each do |action, action_class|
|
47
49
|
li(class: 'nav-item mx-1') {
|
48
|
-
href = route_for(
|
50
|
+
href = TinyAdmin.route_for(slug, reference: reference, action: action)
|
49
51
|
a(href: href, class: 'nav-link btn btn-outline-secondary') {
|
50
52
|
action_class.respond_to?(:title) ? action_class.title : action
|
51
53
|
}
|
@@ -23,14 +23,14 @@ module TinyAdmin
|
|
23
23
|
}
|
24
24
|
div(class: 'collapse navbar-collapse', id: 'navbarNav') {
|
25
25
|
ul(class: 'navbar-nav') {
|
26
|
-
items.each do |
|
26
|
+
items.each do |item|
|
27
27
|
classes = %w[nav-link]
|
28
|
-
classes << 'active' if slug == current_slug
|
29
|
-
link_attributes = { class: classes.join(' '), href: item
|
30
|
-
link_attributes.merge!(item
|
28
|
+
classes << 'active' if item.slug == current_slug
|
29
|
+
link_attributes = { class: classes.join(' '), href: item.path, 'aria-current' => 'page' }
|
30
|
+
link_attributes.merge!(item.options) if item.options
|
31
31
|
|
32
32
|
li(class: 'nav-item') {
|
33
|
-
a(**link_attributes) { item
|
33
|
+
a(**link_attributes) { item.name }
|
34
34
|
}
|
35
35
|
end
|
36
36
|
}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TinyAdmin
|
4
|
+
module Views
|
5
|
+
module Components
|
6
|
+
class Widgets < BasicComponent
|
7
|
+
def initialize(widgets)
|
8
|
+
@widgets = widgets
|
9
|
+
end
|
10
|
+
|
11
|
+
def template
|
12
|
+
return if @widgets.nil? || @widgets.empty?
|
13
|
+
|
14
|
+
div(class: 'container widgets') {
|
15
|
+
@widgets.each_slice(2).each do |row|
|
16
|
+
div(class: 'row') {
|
17
|
+
row.each do |widget|
|
18
|
+
next unless widget < Phlex::HTML
|
19
|
+
|
20
|
+
div(class: 'col') {
|
21
|
+
div(class: 'card') {
|
22
|
+
div(class: 'card-body') {
|
23
|
+
render widget.new
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
27
|
+
end
|
28
|
+
}
|
29
|
+
end
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/tiny_admin.rb
CHANGED
@@ -4,6 +4,7 @@ require 'phlex'
|
|
4
4
|
require 'roda'
|
5
5
|
require 'zeitwerk'
|
6
6
|
|
7
|
+
require 'forwardable'
|
7
8
|
require 'singleton'
|
8
9
|
require 'yaml'
|
9
10
|
|
@@ -23,9 +24,16 @@ module TinyAdmin
|
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
27
|
+
def route_for(section, reference: nil, action: nil, query: nil)
|
28
|
+
root_path = settings.root_path == '/' ? nil : settings.root_path
|
29
|
+
route = [root_path, section, reference, action].compact.join("/")
|
30
|
+
route << "?#{query}" if query
|
31
|
+
route[0] == '/' ? route : route.prepend('/')
|
32
|
+
end
|
33
|
+
|
26
34
|
def settings
|
27
35
|
TinyAdmin::Settings.instance
|
28
36
|
end
|
29
37
|
|
30
|
-
module_function :configure, :configure_from_file, :settings
|
38
|
+
module_function :configure, :configure_from_file, :route_for, :settings
|
31
39
|
end
|
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.
|
4
|
+
version: 0.7.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-
|
11
|
+
date: 2023-05-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: phlex
|
@@ -87,19 +87,23 @@ files:
|
|
87
87
|
- lib/tiny_admin/plugins/no_auth.rb
|
88
88
|
- lib/tiny_admin/plugins/simple_auth.rb
|
89
89
|
- lib/tiny_admin/router.rb
|
90
|
+
- lib/tiny_admin/section.rb
|
90
91
|
- lib/tiny_admin/settings.rb
|
92
|
+
- lib/tiny_admin/store.rb
|
91
93
|
- lib/tiny_admin/support.rb
|
92
94
|
- lib/tiny_admin/utils.rb
|
93
95
|
- lib/tiny_admin/version.rb
|
94
96
|
- lib/tiny_admin/views/actions/index.rb
|
95
97
|
- lib/tiny_admin/views/actions/show.rb
|
96
98
|
- lib/tiny_admin/views/basic_layout.rb
|
99
|
+
- lib/tiny_admin/views/basic_widget.rb
|
97
100
|
- lib/tiny_admin/views/components/basic_component.rb
|
98
101
|
- lib/tiny_admin/views/components/filters_form.rb
|
99
102
|
- lib/tiny_admin/views/components/flash.rb
|
100
103
|
- lib/tiny_admin/views/components/head.rb
|
101
104
|
- lib/tiny_admin/views/components/navbar.rb
|
102
105
|
- lib/tiny_admin/views/components/pagination.rb
|
106
|
+
- lib/tiny_admin/views/components/widgets.rb
|
103
107
|
- lib/tiny_admin/views/default_layout.rb
|
104
108
|
- lib/tiny_admin/views/pages/content.rb
|
105
109
|
- lib/tiny_admin/views/pages/page_not_found.rb
|