tiny_admin 0.6.0 → 0.8.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 +33 -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 +9 -2
- data/lib/tiny_admin/context.rb +9 -14
- data/lib/tiny_admin/field.rb +13 -0
- data/lib/tiny_admin/plugins/active_record_repository.rb +1 -4
- data/lib/tiny_admin/plugins/base_repository.rb +0 -14
- data/lib/tiny_admin/router.rb +52 -37
- data/lib/tiny_admin/section.rb +14 -0
- data/lib/tiny_admin/settings.rb +10 -62
- data/lib/tiny_admin/store.rb +67 -0
- data/lib/tiny_admin/support.rb +4 -0
- data/lib/tiny_admin/utils.rb +9 -13
- data/lib/tiny_admin/version.rb +1 -1
- data/lib/tiny_admin/views/actions/index.rb +18 -15
- data/lib/tiny_admin/views/actions/show.rb +5 -9
- data/lib/tiny_admin/views/basic_layout.rb +5 -1
- data/lib/tiny_admin/views/basic_widget.rb +8 -0
- data/lib/tiny_admin/views/components/field_value.rb +33 -0
- data/lib/tiny_admin/views/components/filters_form.rb +12 -4
- 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/views/pages/simple_auth_login.rb +6 -2
- data/lib/tiny_admin.rb +9 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 71d42ca63e3a410de95780d584cdfb61dd94feb95b503f5f55379efabec9477e
|
4
|
+
data.tar.gz: 96c4b375cb65327635199296d11f54335e6c5266dacce39a7316e0eb2844046c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d377537524247253a871e016d3d5e688e47ab07d40c6d33814c5dfc0be761e734d3de3c8c23d3e3981d3f949ce15d230312b4c71fb2d7d6f33aeb45cc352223f
|
7
|
+
data.tar.gz: 4ddb16c2f1fbfefced95deb426cf369d28201866f800eee1f4b14895bd938233f6f67bf51d784a7edf340b48d7a75230a86b679db9ec71c07f18b726c23971a4
|
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.8'`
|
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,9 @@ 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.
|
87
|
+
|
88
|
+
> 📚 [Wiki Root page](https://github.com/blocknotes/tiny_admin/wiki/Root) available
|
83
89
|
|
84
90
|
Example:
|
85
91
|
|
@@ -87,10 +93,15 @@ Example:
|
|
87
93
|
root:
|
88
94
|
title: MyAdmin
|
89
95
|
redirect: posts
|
96
|
+
widgets:
|
97
|
+
- LatestAuthorsWidget
|
98
|
+
- LatestPostsWidget
|
90
99
|
```
|
91
100
|
|
92
101
|
`helper_class` (String): class or module with helper methods, used for attributes' formatters.
|
93
102
|
|
103
|
+
> 📚 [Wiki Helper methods page](https://github.com/blocknotes/tiny_admin/wiki/Helper-methods) available
|
104
|
+
|
94
105
|
`page_not_found` (String): a view object to render when a missing page is requested.
|
95
106
|
|
96
107
|
`record_not_found` (String): a view object to render when a missing record is requested.
|
@@ -106,11 +117,15 @@ root:
|
|
106
117
|
|
107
118
|
`extra_styles` (String): inline CSS styles.
|
108
119
|
|
120
|
+
> 📚 [Wiki Styles and scripts page](https://github.com/blocknotes/tiny_admin/wiki/Styles-and-scripts) available
|
121
|
+
|
109
122
|
`authentication` (Hash): define the authentication method, properties:
|
110
123
|
|
111
124
|
- `plugin` (String): a plugin class to use (ex. `TinyAdmin::Plugins::SimpleAuth`);
|
112
125
|
- `password` (String): a password hash used by _SimpleAuth_ plugin (generated with `Digest::SHA512.hexdigest("some password")`).
|
113
126
|
|
127
|
+
> 📚 [Wiki Authentication page](https://github.com/blocknotes/tiny_admin/wiki/Authentication) available
|
128
|
+
|
114
129
|
Example:
|
115
130
|
|
116
131
|
```yml
|
@@ -124,8 +139,11 @@ authentication:
|
|
124
139
|
- `slug` (String): section reference identifier;
|
125
140
|
- `name` (String): section's title;
|
126
141
|
- `type` (String): the type of section: `content`, `page`, `resource` or `url`;
|
142
|
+
- `widgets` (Array): list of widgets (as View components) to present;
|
127
143
|
- other properties depends on the section's type.
|
128
144
|
|
145
|
+
> 📚 [Wiki Pages page](https://github.com/blocknotes/tiny_admin/wiki/Pages) available
|
146
|
+
|
129
147
|
For _content_ sections:
|
130
148
|
|
131
149
|
- `content` (String): the HTML content to present.
|
@@ -139,6 +157,9 @@ type: content
|
|
139
157
|
content: >
|
140
158
|
<h1>Test content!</h1>
|
141
159
|
<p>Some test content</p>
|
160
|
+
widgets:
|
161
|
+
- LatestAuthorsWidget
|
162
|
+
- LatestPostsWidget
|
142
163
|
```
|
143
164
|
|
144
165
|
For _url_ sections:
|
@@ -175,10 +196,14 @@ For _resource_ sections:
|
|
175
196
|
|
176
197
|
- `model` (String): the class to use to fetch the data on an item of a collection;
|
177
198
|
- `repository` (String): the class to get the properties related to the model;
|
199
|
+
|
200
|
+
> 📚 [Wiki Repository page](https://github.com/blocknotes/tiny_admin/wiki/Repository) available
|
201
|
+
|
178
202
|
- `index` (Hash): collection's action options (see below);
|
179
203
|
- `show` (Hash): detail's action options (see below);
|
180
204
|
- `collection_actions` (Array of hashes): custom collection's actions;
|
181
205
|
- `member_actions` (Array of hashes): custom details's actions;
|
206
|
+
- `widgets` (Array): list of widgets (as View components) to present;
|
182
207
|
- `only` (Array of strings): list of supported actions (ex. `index`);
|
183
208
|
- `options` (Array of strings): resource options (ex. `hidden`).
|
184
209
|
|
@@ -256,6 +281,9 @@ Example:
|
|
256
281
|
header: The author
|
257
282
|
link_to: authors
|
258
283
|
call: author, name
|
284
|
+
widgets:
|
285
|
+
- LatestAuthorsWidget
|
286
|
+
- LatestPostsWidget
|
259
287
|
```
|
260
288
|
|
261
289
|
### Sample
|
@@ -280,6 +308,9 @@ authentication:
|
|
280
308
|
# password: 'f1891cea80fc05e433c943254c6bdabc159577a02a7395dfebbfbc4f7661d4af56f2d372131a45936de40160007368a56ef216a30cb202c66d3145fd24380906'
|
281
309
|
root:
|
282
310
|
title: Test Admin
|
311
|
+
widgets:
|
312
|
+
- LatestAuthorsWidget
|
313
|
+
- LatestPostsWidget
|
283
314
|
# page: RootPage
|
284
315
|
helper_class: AdminHelper
|
285
316
|
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
|
@@ -12,7 +12,11 @@ module TinyAdmin
|
|
12
12
|
end
|
13
13
|
|
14
14
|
r.post 'unauthenticated' do
|
15
|
-
|
15
|
+
warning = TinyAdmin.settings.helper_class.label_for(
|
16
|
+
'Failed to authenticate',
|
17
|
+
options: ['authentication.unauthenticated']
|
18
|
+
)
|
19
|
+
render_login(warnings: [warning])
|
16
20
|
end
|
17
21
|
|
18
22
|
r.get 'logout' do
|
@@ -24,7 +28,10 @@ module TinyAdmin
|
|
24
28
|
private
|
25
29
|
|
26
30
|
def render_login(notices: nil, warnings: nil, errors: nil)
|
27
|
-
|
31
|
+
login = TinyAdmin.settings.authentication[:login]
|
32
|
+
return unless login
|
33
|
+
|
34
|
+
page = prepare_page(login, options: %i[no_menu compact_layout])
|
28
35
|
page.messages = {
|
29
36
|
notices: notices || flash['notices'],
|
30
37
|
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/field.rb
CHANGED
@@ -16,6 +16,19 @@ module TinyAdmin
|
|
16
16
|
messages.inject(target) { |result, msg| result&.send(msg) } if messages.any?
|
17
17
|
end
|
18
18
|
|
19
|
+
def translate_value(value)
|
20
|
+
if options && options[:method]
|
21
|
+
method, *args = options[:method].split(',').map(&:strip)
|
22
|
+
if options[:converter]
|
23
|
+
Object.const_get(options[:converter]).send(method, value, options: args || [])
|
24
|
+
else
|
25
|
+
TinyAdmin.settings.helper_class.send(method, value, options: args || [])
|
26
|
+
end
|
27
|
+
else
|
28
|
+
value&.to_s
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
19
32
|
class << self
|
20
33
|
def create_field(name:, title: nil, type: nil, options: {})
|
21
34
|
field_name = name.to_s
|
@@ -6,10 +6,7 @@ module TinyAdmin
|
|
6
6
|
def index_record_attrs(record, fields: nil)
|
7
7
|
return record.attributes.transform_values(&:to_s) unless fields
|
8
8
|
|
9
|
-
fields.to_h
|
10
|
-
value = record.send(name)
|
11
|
-
[name, translate_value(value, field)]
|
12
|
-
end
|
9
|
+
fields.to_h { [_1, record.send(_1)] }
|
13
10
|
end
|
14
11
|
|
15
12
|
def index_title
|
@@ -10,20 +10,6 @@ module TinyAdmin
|
|
10
10
|
def initialize(model)
|
11
11
|
@model = model
|
12
12
|
end
|
13
|
-
|
14
|
-
def translate_value(value, field)
|
15
|
-
if field[:method]
|
16
|
-
method, *options = field[:method].split(',').map(&:strip)
|
17
|
-
if field[:converter]
|
18
|
-
converter = Object.const_get(field[:converter])
|
19
|
-
converter.send(method, value, options: options || [])
|
20
|
-
else
|
21
|
-
TinyAdmin.settings.helper_class.send(method, value, options: options || [])
|
22
|
-
end
|
23
|
-
else
|
24
|
-
value&.to_s
|
25
|
-
end
|
26
|
-
end
|
27
13
|
end
|
28
14
|
end
|
29
15
|
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,11 +3,11 @@
|
|
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,
|
10
9
|
%i[authentication login] => Views::Pages::SimpleAuthLogin,
|
10
|
+
%i[components field_value] => Views::Components::FieldValue,
|
11
11
|
%i[components flash] => Views::Components::Flash,
|
12
12
|
%i[components head] => Views::Components::Head,
|
13
13
|
%i[components navbar] => Views::Components::Navbar,
|
@@ -19,7 +19,8 @@ module TinyAdmin
|
|
19
19
|
%i[repository] => Plugins::ActiveRecordRepository,
|
20
20
|
%i[root_path] => '/admin',
|
21
21
|
%i[root page] => Views::Pages::Root,
|
22
|
-
%i[root title] => 'TinyAdmin'
|
22
|
+
%i[root title] => 'TinyAdmin',
|
23
|
+
%i[sections] => []
|
23
24
|
}.freeze
|
24
25
|
|
25
26
|
OPTIONS = %i[
|
@@ -38,6 +39,8 @@ module TinyAdmin
|
|
38
39
|
style_links
|
39
40
|
].freeze
|
40
41
|
|
42
|
+
attr_reader :store
|
43
|
+
|
41
44
|
OPTIONS.each do |option|
|
42
45
|
define_method(option) do
|
43
46
|
self[option]
|
@@ -70,15 +73,14 @@ module TinyAdmin
|
|
70
73
|
end
|
71
74
|
end
|
72
75
|
|
73
|
-
|
74
|
-
context.resources ||= {}
|
75
|
-
self.sections ||= []
|
76
|
+
@store ||= TinyAdmin::Store.new(self)
|
76
77
|
self.root_path = '/' if root_path == ''
|
77
78
|
|
78
79
|
if authentication[:plugin] <= Plugins::SimpleAuth
|
79
|
-
|
80
|
+
logout_path = "#{root_path}/auth/logout"
|
81
|
+
authentication[:logout] ||= TinyAdmin::Section.new(name: 'logout', slug: 'logout', path: logout_path)
|
80
82
|
end
|
81
|
-
|
83
|
+
store.prepare_sections(sections, logout: authentication[:logout])
|
82
84
|
end
|
83
85
|
|
84
86
|
def reset!
|
@@ -97,7 +99,7 @@ module TinyAdmin
|
|
97
99
|
if value.is_a?(Hash)
|
98
100
|
value.each_key do |key2|
|
99
101
|
path = [key, key2]
|
100
|
-
if DEFAULTS[path].is_a?(Class) || DEFAULTS[path].is_a?(Module)
|
102
|
+
if (DEFAULTS[path].is_a?(Class) || DEFAULTS[path].is_a?(Module)) && self[key][key2].is_a?(String)
|
101
103
|
self[key][key2] = Object.const_get(self[key][key2])
|
102
104
|
end
|
103
105
|
end
|
@@ -105,59 +107,5 @@ module TinyAdmin
|
|
105
107
|
self[key] = Object.const_get(self[key])
|
106
108
|
end
|
107
109
|
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
110
|
end
|
163
111
|
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/support.rb
CHANGED
data/lib/tiny_admin/utils.rb
CHANGED
@@ -14,37 +14,33 @@ 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
|
-
def
|
40
|
+
def humanize(string)
|
41
41
|
return '' unless string
|
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
|
@@ -65,13 +69,7 @@ module TinyAdmin
|
|
65
69
|
attributes.each do |key, value|
|
66
70
|
field = fields[key]
|
67
71
|
td(class: "field-value-#{field.name} field-value-type-#{field.type}") {
|
68
|
-
|
69
|
-
a(href: route_for(field.options[:link_to], reference: value)) {
|
70
|
-
field.apply_call_option(record) || value
|
71
|
-
}
|
72
|
-
else
|
73
|
-
value
|
74
|
-
end
|
72
|
+
render TinyAdmin.settings.components[:field_value].new(field, value, record: record)
|
75
73
|
}
|
76
74
|
end
|
77
75
|
|
@@ -82,15 +80,20 @@ module TinyAdmin
|
|
82
80
|
links.each do |link|
|
83
81
|
whitespace
|
84
82
|
if link == 'show'
|
85
|
-
a(href: route_for(
|
83
|
+
a(href: TinyAdmin.route_for(slug, reference: record.id), class: link_class) {
|
84
|
+
label_for('Show', options: ['actions.index.links.show'])
|
85
|
+
}
|
86
86
|
else
|
87
|
-
a(href: route_for(
|
88
|
-
|
87
|
+
a(href: TinyAdmin.route_for(slug, reference: record.id, action: link), class: link_class) {
|
88
|
+
fallback = humanize(link)
|
89
|
+
label_for(fallback, options: ["actions.index.links.#{link}"])
|
89
90
|
}
|
90
91
|
end
|
91
92
|
end
|
92
93
|
else
|
93
|
-
a(href: route_for(
|
94
|
+
a(href: TinyAdmin.route_for(slug, reference: record.id), class: link_class) {
|
95
|
+
label_for('Show', options: ['actions.index.links.show'])
|
96
|
+
}
|
94
97
|
end
|
95
98
|
}
|
96
99
|
}
|
@@ -103,7 +106,7 @@ module TinyAdmin
|
|
103
106
|
ul(class: 'nav justify-content-end') {
|
104
107
|
(actions || {}).each do |action, action_class|
|
105
108
|
li(class: 'nav-item mx-1') {
|
106
|
-
href = route_for(
|
109
|
+
href = TinyAdmin.route_for(slug, action: action)
|
107
110
|
a(href: href, class: 'nav-link btn btn-outline-secondary') {
|
108
111
|
action_class.respond_to?(:title) ? action_class.title : action
|
109
112
|
}
|
@@ -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
|
@@ -25,16 +25,12 @@ module TinyAdmin
|
|
25
25
|
div(class: 'field-header col-2') { field.options[:header] || field.title }
|
26
26
|
end
|
27
27
|
div(class: 'field-value col-10') {
|
28
|
-
|
29
|
-
a(href: route_for(field.options[:link_to], reference: value)) {
|
30
|
-
field.apply_call_option(record) || value
|
31
|
-
}
|
32
|
-
else
|
33
|
-
value
|
34
|
-
end
|
28
|
+
render TinyAdmin.settings.components[:field_value].new(field, value, record: record)
|
35
29
|
}
|
36
30
|
}
|
37
31
|
end
|
32
|
+
|
33
|
+
render TinyAdmin::Views::Components::Widgets.new(widgets)
|
38
34
|
}
|
39
35
|
end
|
40
36
|
end
|
@@ -45,7 +41,7 @@ module TinyAdmin
|
|
45
41
|
ul(class: 'nav justify-content-end') {
|
46
42
|
(actions || {}).each do |action, action_class|
|
47
43
|
li(class: 'nav-item mx-1') {
|
48
|
-
href = route_for(
|
44
|
+
href = TinyAdmin.route_for(slug, reference: reference, action: action)
|
49
45
|
a(href: href, class: 'nav-link btn btn-outline-secondary') {
|
50
46
|
action_class.respond_to?(:title) ? action_class.title : action
|
51
47
|
}
|
@@ -5,7 +5,11 @@ module TinyAdmin
|
|
5
5
|
class BasicLayout < Phlex::HTML
|
6
6
|
include Utils
|
7
7
|
|
8
|
-
attr_accessor :content
|
8
|
+
attr_accessor :content, :widgets
|
9
|
+
|
10
|
+
def label_for(value, options: [])
|
11
|
+
TinyAdmin.settings.helper_class.label_for(value, options: options)
|
12
|
+
end
|
9
13
|
|
10
14
|
def update_attributes(attributes)
|
11
15
|
attributes.each do |key, value|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TinyAdmin
|
4
|
+
module Views
|
5
|
+
module Components
|
6
|
+
class FieldValue < BasicComponent
|
7
|
+
attr_reader :field, :value, :record
|
8
|
+
|
9
|
+
def initialize(field, value, record:)
|
10
|
+
@field = field
|
11
|
+
@value = value
|
12
|
+
@record = record
|
13
|
+
end
|
14
|
+
|
15
|
+
def template
|
16
|
+
translated_value = field.translate_value(value)
|
17
|
+
value_class = field.options[:options]&.include?('value_class') ? "value-#{value}" : nil
|
18
|
+
if field.options[:link_to]
|
19
|
+
a(href: TinyAdmin.route_for(field.options[:link_to], reference: translated_value)) {
|
20
|
+
span(class: value_class) {
|
21
|
+
field.apply_call_option(record) || translated_value
|
22
|
+
}
|
23
|
+
}
|
24
|
+
else
|
25
|
+
span(class: value_class) {
|
26
|
+
translated_value
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -17,8 +17,12 @@ module TinyAdmin
|
|
17
17
|
when :boolean
|
18
18
|
select(class: 'form-select', id: "filter-#{name}", name: "q[#{name}]") {
|
19
19
|
option(value: '') { '-' }
|
20
|
-
option(value: '0', selected: filter[:value] == '0') {
|
21
|
-
|
20
|
+
option(value: '0', selected: filter[:value] == '0') {
|
21
|
+
TinyAdmin.settings.helper_class.label_for('false', options: ['components.filters_form.boolean.false'])
|
22
|
+
}
|
23
|
+
option(value: '1', selected: filter[:value] == '1') {
|
24
|
+
TinyAdmin.settings.helper_class.label_for('true', options: ['components.filters_form.boolean.true'])
|
25
|
+
}
|
22
26
|
}
|
23
27
|
when :date
|
24
28
|
input(type: 'date', class: 'form-control', id: "filter-#{name}", name: "q[#{name}]", value: filter[:value])
|
@@ -40,9 +44,13 @@ module TinyAdmin
|
|
40
44
|
end
|
41
45
|
|
42
46
|
div(class: 'mt-3') {
|
43
|
-
a(href: section_path, class: 'button_clear btn btn-secondary text-white') {
|
47
|
+
a(href: section_path, class: 'button_clear btn btn-secondary text-white') {
|
48
|
+
TinyAdmin.settings.helper_class.label_for('Clear', options: ['components.filters_form.buttons.clear'])
|
49
|
+
}
|
44
50
|
whitespace
|
45
|
-
button(type: 'submit', class: 'button_filter btn btn-secondary') {
|
51
|
+
button(type: 'submit', class: 'button_filter btn btn-secondary') {
|
52
|
+
TinyAdmin.settings.helper_class.label_for('Filter', options: ['components.filters_form.buttons.submit'])
|
53
|
+
}
|
46
54
|
}
|
47
55
|
}
|
48
56
|
end
|
@@ -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
|
@@ -11,12 +11,16 @@ module TinyAdmin
|
|
11
11
|
|
12
12
|
form(class: 'form_login', method: 'post') {
|
13
13
|
div(class: 'mt-3') {
|
14
|
-
label(for: 'secret', class: 'form-label') {
|
14
|
+
label(for: 'secret', class: 'form-label') {
|
15
|
+
label_for('Password', options: ['pages.simple_auth_login.inputs.password'])
|
16
|
+
}
|
15
17
|
input(type: 'password', name: 'secret', class: 'form-control', id: 'secret')
|
16
18
|
}
|
17
19
|
|
18
20
|
div(class: 'mt-3') {
|
19
|
-
button(type: 'submit', class: 'button_login btn btn-primary') {
|
21
|
+
button(type: 'submit', class: 'button_login btn btn-primary') {
|
22
|
+
label_for('Login', options: ['pages.simple_auth_login.buttons.submit'])
|
23
|
+
}
|
20
24
|
}
|
21
25
|
}
|
22
26
|
}
|
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.8.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-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: phlex
|
@@ -87,19 +87,24 @@ 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
|
101
|
+
- lib/tiny_admin/views/components/field_value.rb
|
98
102
|
- lib/tiny_admin/views/components/filters_form.rb
|
99
103
|
- lib/tiny_admin/views/components/flash.rb
|
100
104
|
- lib/tiny_admin/views/components/head.rb
|
101
105
|
- lib/tiny_admin/views/components/navbar.rb
|
102
106
|
- lib/tiny_admin/views/components/pagination.rb
|
107
|
+
- lib/tiny_admin/views/components/widgets.rb
|
103
108
|
- lib/tiny_admin/views/default_layout.rb
|
104
109
|
- lib/tiny_admin/views/pages/content.rb
|
105
110
|
- lib/tiny_admin/views/pages/page_not_found.rb
|