tiny_admin 0.6.0 → 0.7.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 +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
|
-
[](https://badge.fury.io/rb/tiny_admin)
|
3
|
+
[](https://badge.fury.io/rb/tiny_admin)
|
4
|
+
[](https://rubygems.org/gems/tiny_admin)
|
5
|
+
[](https://github.com/blocknotes/tiny_admin/actions/workflows/linters.yml)
|
6
|
+
[](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
|