tiny_admin 0.4.0 → 0.5.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 +123 -43
- data/lib/tiny_admin/actions/index.rb +7 -4
- data/lib/tiny_admin/actions/show.rb +5 -6
- data/lib/tiny_admin/context.rb +10 -1
- data/lib/tiny_admin/field.rb +8 -7
- data/lib/tiny_admin/plugins/active_record_repository.rb +2 -2
- data/lib/tiny_admin/router.rb +9 -8
- data/lib/tiny_admin/settings.rb +51 -32
- data/lib/tiny_admin/support.rb +4 -4
- data/lib/tiny_admin/utils.rb +11 -4
- data/lib/tiny_admin/version.rb +1 -1
- data/lib/tiny_admin/views/actions/index.rb +29 -10
- data/lib/tiny_admin/views/actions/show.rb +7 -6
- data/lib/tiny_admin/views/components/basic_component.rb +15 -0
- data/lib/tiny_admin/views/components/filters_form.rb +2 -7
- data/lib/tiny_admin/views/components/flash.rb +7 -10
- data/lib/tiny_admin/views/components/head.rb +2 -8
- data/lib/tiny_admin/views/components/navbar.rb +15 -14
- data/lib/tiny_admin/views/components/pagination.rb +2 -8
- data/lib/tiny_admin/views/default_layout.rb +3 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3ac2be067ca35c35f40c677c5f9461c5f83e366488a48e6ab50a888f0f22317
|
4
|
+
data.tar.gz: 2289dd33940174e8e614260f96273967ade51f75dc67a04d5e639415c984e65b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4f80d86ac6b30bc29cf8e2f1efe480edefe84907bf5e9b7a3a8c3b3c3750ff59a1f9e609c87090105c0cd31fa22df7aa913858911a455d918633269c27317e7
|
7
|
+
data.tar.gz: 4fe8a75d9e3c0cc3bd756ab247fecb7575049dcec58559bdb7cdb1c58ecb4b6e7de3f8be96c396b0b304134cc75c6058b3cbeafce92cd027413971a99a812c02
|
data/README.md
CHANGED
@@ -16,10 +16,10 @@ Please ⭐ if you like it.
|
|
16
16
|
|
17
17
|
## Install
|
18
18
|
|
19
|
-
- Add to your Gemfile: `gem 'tiny_admin', '~> 0.
|
19
|
+
- Add to your Gemfile: `gem 'tiny_admin', '~> 0.5'`
|
20
20
|
- Mount the app in a route (check some examples with: Hanami, Rails, Roda and standalone in [extra](extra))
|
21
21
|
+ in Rails, update _config/routes.rb_: `mount TinyAdmin::Router => '/admin'`
|
22
|
-
- Configure the dashboard using `TinyAdmin.configure` and/or `TinyAdmin.configure_from_file` (see [configuration](#configuration) below):
|
22
|
+
- Configure the dashboard using `TinyAdmin.configure` and/or `TinyAdmin.configure_from_file` with a YAML config file (see [configuration](#configuration) below):
|
23
23
|
|
24
24
|
```rb
|
25
25
|
TinyAdmin.configure do |settings|
|
@@ -35,32 +35,37 @@ end
|
|
35
35
|
### Authentication
|
36
36
|
|
37
37
|
Plugins available:
|
38
|
-
|
39
|
-
-
|
38
|
+
|
39
|
+
- **SimpleAuth**: a session authentication based on Warden (`warden` gem must be included in the Gemfile) using a password hash provided via config or via environment variable (`ADMIN_PASSWORD_HASH`). _Disclaimer: this plugin is provided as example, if you need a secure authentication I suggest to create your own._
|
40
|
+
|
41
|
+
- **NoAuth**: no authentication.
|
40
42
|
|
41
43
|
### Repository
|
42
44
|
|
43
45
|
Plugin available:
|
44
|
-
|
46
|
+
|
47
|
+
- **ActiveRecordRepository**: isolates the query layer to expose the resources in the admin interface.
|
45
48
|
|
46
49
|
### View pages
|
47
50
|
|
48
51
|
Pages available:
|
49
|
-
|
50
|
-
-
|
51
|
-
-
|
52
|
-
-
|
53
|
-
-
|
54
|
-
-
|
52
|
+
|
53
|
+
- **Root**: define how to present the content in the main page of the interface;
|
54
|
+
- **PageNotFound**: define how to present pages not found;
|
55
|
+
- **RecordNotFound**: define how to present record not found page;
|
56
|
+
- **SimpleAuthLogin**: define how to present the login form for SimpleAuth plugin;
|
57
|
+
- **Index**: define how to present a collection of items;
|
58
|
+
- **Show**: define how to present the details of an item.
|
55
59
|
|
56
60
|
### View components
|
57
61
|
|
58
62
|
Components available:
|
59
|
-
|
60
|
-
-
|
61
|
-
-
|
62
|
-
-
|
63
|
-
-
|
63
|
+
|
64
|
+
- **FiltersForm**: define how to present the filters form in the resource collection pages;
|
65
|
+
- **Flash**: define how to present the flash messages;
|
66
|
+
- **Head**: define how to present the Head tag;
|
67
|
+
- **Navbar**: define how to present the navbar (the default one uses the Bootstrap structure);
|
68
|
+
- **Pagination**: define how to present the pagination of a collection.
|
64
69
|
|
65
70
|
## Configuration
|
66
71
|
|
@@ -70,9 +75,10 @@ See [extra](extra) folder for some usage examples.
|
|
70
75
|
The following options are supported:
|
71
76
|
|
72
77
|
`root` (Hash): define the root section of the admin, properties:
|
73
|
-
|
74
|
-
|
75
|
-
|
78
|
+
|
79
|
+
- `title` (String): root section's title;
|
80
|
+
- `page` (String): a view object to render;
|
81
|
+
- `redirect` (String): alternative to _page_ option - redirects to a specific slug;
|
76
82
|
|
77
83
|
Example:
|
78
84
|
|
@@ -88,9 +94,21 @@ root:
|
|
88
94
|
|
89
95
|
`record_not_found` (String): a view object to render when a missing record is requested.
|
90
96
|
|
97
|
+
`style_links` (Array of hashes): list of styles files to include, properties:
|
98
|
+
|
99
|
+
- `href` (String): URL for the style file;
|
100
|
+
- `rel` (String): type of style file.
|
101
|
+
|
102
|
+
`scripts` (Array of hashes): list of scripts to include, properties:
|
103
|
+
|
104
|
+
- `src` (String): source URL for the script.
|
105
|
+
|
106
|
+
`extra_styles` (String): inline CSS styles.
|
107
|
+
|
91
108
|
`authentication` (Hash): define the authentication method, properties:
|
92
|
-
|
93
|
-
|
109
|
+
|
110
|
+
- `plugin` (String): a plugin class to use (ex. `TinyAdmin::Plugins::SimpleAuth`);
|
111
|
+
- `password` (String): a password hash used by _SimpleAuth_ plugin (generated with `Digest::SHA512.hexdigest("some password")`).
|
94
112
|
|
95
113
|
Example:
|
96
114
|
|
@@ -101,15 +119,17 @@ authentication:
|
|
101
119
|
```
|
102
120
|
|
103
121
|
`sections` (Array of hashes): define the admin sections, properties:
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
122
|
+
|
123
|
+
- `slug` (String): section reference identifier;
|
124
|
+
- `name` (String): section's title;
|
125
|
+
- `type` (String): the type of section: `url`, `page` or `resource`;
|
126
|
+
- other properties depends on the section's type.
|
108
127
|
|
109
128
|
For _url_ sections:
|
110
|
-
|
111
|
-
|
112
|
-
|
129
|
+
|
130
|
+
- `url` (String): the URL to load when clicking on the section's menu item;
|
131
|
+
- `options` (Hash): properties:
|
132
|
+
+ `target` (String): link _target_ attributes (ex. `_blank`).
|
113
133
|
|
114
134
|
Example:
|
115
135
|
|
@@ -123,7 +143,8 @@ options:
|
|
123
143
|
```
|
124
144
|
|
125
145
|
For _page_ sections:
|
126
|
-
|
146
|
+
|
147
|
+
- `page` (String): a view object to render.
|
127
148
|
|
128
149
|
Example:
|
129
150
|
|
@@ -135,14 +156,15 @@ page: Admin::Stats
|
|
135
156
|
```
|
136
157
|
|
137
158
|
For _resource_ sections:
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
159
|
+
|
160
|
+
- `model` (String): the class to use to fetch the data on an item of a collection;
|
161
|
+
- `repository` (String): the class to get the properties related to the model;
|
162
|
+
- `index` (Hash): collection's action options (see below);
|
163
|
+
- `show` (Hash): detail's action options (see below);
|
164
|
+
- `collection_actions` (Array of hashes): custom collection's actions;
|
165
|
+
- `member_actions` (Array of hashes): custom details's actions;
|
166
|
+
- `only` (Array of strings): list of supported actions (ex. `index`);
|
167
|
+
- `options` (Array of strings): resource options (ex. `hidden`).
|
146
168
|
|
147
169
|
Example:
|
148
170
|
|
@@ -153,14 +175,72 @@ type: resource
|
|
153
175
|
model: Post
|
154
176
|
```
|
155
177
|
|
156
|
-
|
157
|
-
- `href` (String): URL for the style file;
|
158
|
-
- `rel` (String): type of style file.
|
178
|
+
#### Resource index options
|
159
179
|
|
160
|
-
|
161
|
-
- `src` (String): source URL for the script.
|
180
|
+
The Index hash supports the following options:
|
162
181
|
|
163
|
-
`
|
182
|
+
- `attributes` (Array): fields to expose in the resource list page;
|
183
|
+
- `filters` (Array): filter the current listing;
|
184
|
+
- `links` (Array): custom member actions to expose for each list's entry (defined in _member_actions_);
|
185
|
+
- `pagination` (Integer): max pages size;
|
186
|
+
- `sort` (Array): sort options to pass to the listing query.
|
187
|
+
|
188
|
+
Example:
|
189
|
+
|
190
|
+
```yml
|
191
|
+
index:
|
192
|
+
sort:
|
193
|
+
- id DESC
|
194
|
+
pagination: 10
|
195
|
+
attributes:
|
196
|
+
- id
|
197
|
+
- author: call, name
|
198
|
+
- position: round, 1
|
199
|
+
- field: author_id
|
200
|
+
header: The author
|
201
|
+
link_to: authors
|
202
|
+
call: author, name
|
203
|
+
filters:
|
204
|
+
- title
|
205
|
+
- field: state
|
206
|
+
type: select
|
207
|
+
values:
|
208
|
+
- published
|
209
|
+
- draft
|
210
|
+
- archived
|
211
|
+
links:
|
212
|
+
- show
|
213
|
+
- author_posts
|
214
|
+
- csv_export
|
215
|
+
```
|
216
|
+
|
217
|
+
#### Resource show options
|
218
|
+
|
219
|
+
The Show hash supports the following options:
|
220
|
+
|
221
|
+
- `attributes` (Array): fields to expose in the resource details page.
|
222
|
+
|
223
|
+
Example:
|
224
|
+
|
225
|
+
```yml
|
226
|
+
show:
|
227
|
+
attributes:
|
228
|
+
# Expose the id column
|
229
|
+
- id
|
230
|
+
# Expose the title column, calling `downcase` support method
|
231
|
+
- title: downcase
|
232
|
+
# Expose the category column, calling `upcase` support method
|
233
|
+
- category: upcase
|
234
|
+
# Expose the position column, calling `format` support method with argument %f
|
235
|
+
- position: format, %f
|
236
|
+
# Expose the position created_at, calling `strftime` support method with argument %Y%m%d %H:%M
|
237
|
+
- created_at: strftime, %Y%m%d %H:%M
|
238
|
+
# Expose the author_id column, with a custom header label, linked to authors section and calling author.name to get the value
|
239
|
+
- field: author_id
|
240
|
+
header: The author
|
241
|
+
link_to: authors
|
242
|
+
call: author, name
|
243
|
+
```
|
164
244
|
|
165
245
|
### Sample
|
166
246
|
|
@@ -6,6 +6,7 @@ module TinyAdmin
|
|
6
6
|
attr_reader :current_page,
|
7
7
|
:fields_options,
|
8
8
|
:filters_list,
|
9
|
+
:links,
|
9
10
|
:pagination,
|
10
11
|
:pages,
|
11
12
|
:params,
|
@@ -13,7 +14,7 @@ module TinyAdmin
|
|
13
14
|
:repository,
|
14
15
|
:sort
|
15
16
|
|
16
|
-
def call(app:, context:, options
|
17
|
+
def call(app:, context:, options:)
|
17
18
|
evaluate_options(options)
|
18
19
|
fields = repository.fields(options: fields_options)
|
19
20
|
filters = prepare_filters(fields, filters_list)
|
@@ -22,9 +23,10 @@ module TinyAdmin
|
|
22
23
|
prepare_page(Views::Actions::Index) do |page|
|
23
24
|
setup_pagination(page, settings.components[:pagination], total_count: total_count)
|
24
25
|
page.update_attributes(
|
25
|
-
actions: actions,
|
26
|
+
actions: context.actions,
|
26
27
|
fields: fields,
|
27
28
|
filters: filters,
|
29
|
+
links: links,
|
28
30
|
prepare_record: ->(record) { repository.index_record_attrs(record, fields: fields_options) },
|
29
31
|
records: records,
|
30
32
|
title: repository.index_title
|
@@ -40,7 +42,8 @@ module TinyAdmin
|
|
40
42
|
@repository = context.repository
|
41
43
|
@filters_list = options[:filters]
|
42
44
|
@pagination = options[:pagination] || 10
|
43
|
-
@sort = options[:sort]
|
45
|
+
@sort = options[:sort]
|
46
|
+
@links = options[:links]
|
44
47
|
|
45
48
|
@current_page = (params['p'] || 1).to_i
|
46
49
|
@query_string = params_to_s(params.except('p'))
|
@@ -60,7 +63,7 @@ module TinyAdmin
|
|
60
63
|
return if pages <= 1 || !pagination_component_class
|
61
64
|
|
62
65
|
page.pagination_component = pagination_component_class.new
|
63
|
-
page.pagination_component.
|
66
|
+
page.pagination_component.update_attributes(current: current_page, pages: pages, query_string: query_string)
|
64
67
|
end
|
65
68
|
end
|
66
69
|
end
|
@@ -3,18 +3,17 @@
|
|
3
3
|
module TinyAdmin
|
4
4
|
module Actions
|
5
5
|
class Show < BasicAction
|
6
|
-
|
7
|
-
|
8
|
-
def call(app:, context:, options:, actions:)
|
9
|
-
@repository = context.repository
|
6
|
+
def call(app:, context:, options:)
|
10
7
|
fields_options = attribute_options(options[:attributes])
|
8
|
+
repository = context.repository
|
11
9
|
record = repository.find(context.reference)
|
10
|
+
prepare_record = ->(record_data) { repository.show_record_attrs(record_data, fields: fields_options) }
|
12
11
|
|
13
12
|
prepare_page(Views::Actions::Show) do |page|
|
14
13
|
page.update_attributes(
|
15
|
-
actions: actions,
|
14
|
+
actions: context.actions,
|
16
15
|
fields: repository.fields(options: fields_options),
|
17
|
-
prepare_record:
|
16
|
+
prepare_record: prepare_record,
|
18
17
|
record: record,
|
19
18
|
title: repository.show_title(record)
|
20
19
|
)
|
data/lib/tiny_admin/context.rb
CHANGED
@@ -4,6 +4,15 @@ module TinyAdmin
|
|
4
4
|
class Context
|
5
5
|
include Singleton
|
6
6
|
|
7
|
-
attr_accessor :
|
7
|
+
attr_accessor :actions,
|
8
|
+
:navbar,
|
9
|
+
:pages,
|
10
|
+
:reference,
|
11
|
+
:repository,
|
12
|
+
:request,
|
13
|
+
:resources,
|
14
|
+
:router,
|
15
|
+
:settings,
|
16
|
+
:slug
|
8
17
|
end
|
9
18
|
end
|
data/lib/tiny_admin/field.rb
CHANGED
@@ -7,19 +7,20 @@ module TinyAdmin
|
|
7
7
|
def initialize(name:, title:, type:, options: {})
|
8
8
|
@type = type
|
9
9
|
@name = name
|
10
|
-
@title = title
|
10
|
+
@title = title
|
11
11
|
@options = options
|
12
12
|
end
|
13
13
|
|
14
|
+
def apply_call_option(target)
|
15
|
+
messages = (options[:call] || '').split(',').map(&:strip)
|
16
|
+
messages.inject(target) { |result, msg| result&.send(msg) } if messages.any?
|
17
|
+
end
|
18
|
+
|
14
19
|
class << self
|
15
20
|
def create_field(name:, title: nil, type: nil, options: {})
|
16
21
|
field_name = name.to_s
|
17
|
-
|
18
|
-
|
19
|
-
title: title || field_name.respond_to?(:humanize) ? field_name.humanize : field_name.tr('_', ' ').capitalize,
|
20
|
-
type: type || :string,
|
21
|
-
options: options
|
22
|
-
)
|
22
|
+
field_title = field_name.respond_to?(:humanize) ? field_name.humanize : field_name.tr('_', ' ').capitalize
|
23
|
+
new(name: field_name, title: title || field_title, type: type || :string, options: options || {})
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
@@ -42,8 +42,8 @@ module TinyAdmin
|
|
42
42
|
raise BaseRepository::RecordNotFound, e.message
|
43
43
|
end
|
44
44
|
|
45
|
-
def list(page: 1, limit: 10, sort:
|
46
|
-
query = model.all.order(sort)
|
45
|
+
def list(page: 1, limit: 10, sort: nil, filters: nil)
|
46
|
+
query = sort ? model.all.order(sort) : model.all
|
47
47
|
query = apply_filters(query, filters) if filters
|
48
48
|
page_offset = page.positive? ? (page - 1) * limit : 0
|
49
49
|
records = query.offset(page_offset).limit(limit).to_a
|
data/lib/tiny_admin/router.rb
CHANGED
@@ -29,11 +29,11 @@ module TinyAdmin
|
|
29
29
|
r.redirect settings.root_path
|
30
30
|
end
|
31
31
|
|
32
|
-
context.
|
32
|
+
context.pages.each do |slug, data|
|
33
33
|
setup_page_route(r, slug, data)
|
34
34
|
end
|
35
35
|
|
36
|
-
context.
|
36
|
+
context.resources.each do |slug, options|
|
37
37
|
setup_resource_routes(r, slug, options: options || {})
|
38
38
|
end
|
39
39
|
|
@@ -88,12 +88,12 @@ module TinyAdmin
|
|
88
88
|
)
|
89
89
|
|
90
90
|
# Index
|
91
|
-
|
92
|
-
if !actions || actions.include?(:index) || actions.include?('index')
|
91
|
+
if options[:only].include?(:index) || options[:only].include?('index')
|
93
92
|
router.is do
|
93
|
+
context.actions = custom_actions
|
94
94
|
context.request = request
|
95
95
|
index_action = TinyAdmin::Actions::Index.new
|
96
|
-
render_page index_action.call(app: self, context: context, options: action_options
|
96
|
+
render_page index_action.call(app: self, context: context, options: action_options)
|
97
97
|
end
|
98
98
|
end
|
99
99
|
end
|
@@ -114,12 +114,12 @@ module TinyAdmin
|
|
114
114
|
)
|
115
115
|
|
116
116
|
# Show
|
117
|
-
|
118
|
-
if !actions || actions.include?(:show) || actions.include?('show')
|
117
|
+
if options[:only].include?(:show) || options[:only].include?('show')
|
119
118
|
router.is do
|
119
|
+
context.actions = custom_actions
|
120
120
|
context.request = request
|
121
121
|
show_action = TinyAdmin::Actions::Show.new
|
122
|
-
render_page show_action.call(app: self, context: context, options: action_options
|
122
|
+
render_page show_action.call(app: self, context: context, options: action_options)
|
123
123
|
end
|
124
124
|
end
|
125
125
|
end
|
@@ -132,6 +132,7 @@ module TinyAdmin
|
|
132
132
|
action_class = action.is_a?(String) ? Object.const_get(action) : action
|
133
133
|
|
134
134
|
router.get action_slug.to_s do
|
135
|
+
context.actions = {}
|
135
136
|
context.request = request
|
136
137
|
custom_action = action_class.new
|
137
138
|
render_page custom_action.call(app: self, context: context, options: options)
|
data/lib/tiny_admin/settings.rb
CHANGED
@@ -25,7 +25,6 @@ module TinyAdmin
|
|
25
25
|
:components,
|
26
26
|
:extra_styles,
|
27
27
|
:helper_class,
|
28
|
-
:navbar,
|
29
28
|
:page_not_found,
|
30
29
|
:record_not_found,
|
31
30
|
:repository,
|
@@ -35,8 +34,6 @@ module TinyAdmin
|
|
35
34
|
:scripts,
|
36
35
|
:style_links
|
37
36
|
|
38
|
-
attr_reader :pages, :resources
|
39
|
-
|
40
37
|
def [](key)
|
41
38
|
send(key)
|
42
39
|
end
|
@@ -57,39 +54,15 @@ module TinyAdmin
|
|
57
54
|
end
|
58
55
|
end
|
59
56
|
|
60
|
-
|
61
|
-
|
57
|
+
context.pages ||= {}
|
58
|
+
context.resources ||= {}
|
62
59
|
@sections ||= []
|
63
60
|
@root_path = '/' if @root_path == ''
|
64
|
-
if @authentication[:plugin] == Plugins::SimpleAuth
|
65
|
-
@authentication[:logout] ||= ['logout', "#{root_path}/auth/logout"]
|
66
|
-
end
|
67
|
-
@navbar = prepare_navbar(sections, logout: authentication[:logout])
|
68
|
-
end
|
69
61
|
|
70
|
-
|
71
|
-
|
72
|
-
slug = section[:slug]
|
73
|
-
case section[:type]&.to_sym
|
74
|
-
when :url
|
75
|
-
list[slug] = [section[:name], section[:url], section[:options]]
|
76
|
-
when :page
|
77
|
-
page = section[:page]
|
78
|
-
pages[slug] = page.is_a?(String) ? Object.const_get(page) : page
|
79
|
-
list[slug] = [section[:name], route_for(slug)]
|
80
|
-
when :resource
|
81
|
-
repository = section[:repository] || settings.repository
|
82
|
-
resources[slug] = {
|
83
|
-
model: section[:model].is_a?(String) ? Object.const_get(section[:model]) : section[:model],
|
84
|
-
repository: repository.is_a?(String) ? Object.const_get(repository) : repository
|
85
|
-
}
|
86
|
-
resources[slug].merge! section.slice(:resource, :only, :index, :show, :collection_actions, :member_actions)
|
87
|
-
hidden = section[:options] && (section[:options].include?(:hidden) || section[:options].include?('hidden'))
|
88
|
-
list[slug] = [section[:name], route_for(slug)] unless hidden
|
89
|
-
end
|
62
|
+
if @authentication[:plugin] <= Plugins::SimpleAuth
|
63
|
+
@authentication[:logout] ||= { name: 'logout', path: "#{root_path}/auth/logout" }
|
90
64
|
end
|
91
|
-
|
92
|
-
items
|
65
|
+
context.navbar = prepare_navbar(sections, logout: authentication[:logout])
|
93
66
|
end
|
94
67
|
|
95
68
|
private
|
@@ -106,5 +79,51 @@ module TinyAdmin
|
|
106
79
|
self[key] = Object.const_get(self[key])
|
107
80
|
end
|
108
81
|
end
|
82
|
+
|
83
|
+
def prepare_navbar(sections, logout:)
|
84
|
+
items = sections.each_with_object({}) do |section, list|
|
85
|
+
unless section.is_a?(Hash)
|
86
|
+
section_class = Object.const_get(section)
|
87
|
+
next unless section_class.respond_to?(:to_h)
|
88
|
+
|
89
|
+
section = section_class.to_h
|
90
|
+
end
|
91
|
+
|
92
|
+
slug = section[:slug].to_s
|
93
|
+
case section[:type]&.to_sym
|
94
|
+
when :url
|
95
|
+
list[slug] = add_url_section(slug, section)
|
96
|
+
when :page
|
97
|
+
list[slug] = add_page_section(slug, section)
|
98
|
+
when :resource
|
99
|
+
list[slug] = add_resource_section(slug, section)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
items['auth/logout'] = logout if logout
|
103
|
+
items
|
104
|
+
end
|
105
|
+
|
106
|
+
def add_url_section(_slug, section)
|
107
|
+
section.slice(:name, :options).tap { _1[:path] = section[:url] }
|
108
|
+
end
|
109
|
+
|
110
|
+
def add_page_section(slug, section)
|
111
|
+
page = section[:page]
|
112
|
+
context.pages[slug] = page.is_a?(String) ? Object.const_get(page) : page
|
113
|
+
{ name: section[:name], path: route_for(slug), class: context.pages[slug] }
|
114
|
+
end
|
115
|
+
|
116
|
+
def add_resource_section(slug, section)
|
117
|
+
repository = section[:repository] || settings.repository
|
118
|
+
context.resources[slug] = {
|
119
|
+
model: section[:model].is_a?(String) ? Object.const_get(section[:model]) : section[:model],
|
120
|
+
repository: repository.is_a?(String) ? Object.const_get(repository) : repository
|
121
|
+
}
|
122
|
+
resource_options = section.slice(:resource, :only, :index, :show, :collection_actions, :member_actions)
|
123
|
+
resource_options[:only] ||= %i[index show]
|
124
|
+
context.resources[slug].merge!(resource_options)
|
125
|
+
hidden = section[:options] && (section[:options].include?(:hidden) || section[:options].include?('hidden'))
|
126
|
+
{ name: section[:name], path: route_for(slug) } unless hidden
|
127
|
+
end
|
109
128
|
end
|
110
129
|
end
|
data/lib/tiny_admin/support.rb
CHANGED
@@ -4,7 +4,7 @@ module TinyAdmin
|
|
4
4
|
class Support
|
5
5
|
class << self
|
6
6
|
def call(value, options: [])
|
7
|
-
|
7
|
+
options.inject(value) { |result, message| result&.send(message) } if value && options&.any?
|
8
8
|
end
|
9
9
|
|
10
10
|
def downcase(value, options: [])
|
@@ -12,7 +12,7 @@ module TinyAdmin
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def format(value, options: [])
|
15
|
-
|
15
|
+
Kernel.format(options.first, value) if value && options&.any?
|
16
16
|
end
|
17
17
|
|
18
18
|
def round(value, options: [])
|
@@ -20,11 +20,11 @@ module TinyAdmin
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def strftime(value, options: [])
|
23
|
-
value
|
23
|
+
value&.strftime(options&.first || '%Y-%m-%d %H:%M')
|
24
24
|
end
|
25
25
|
|
26
26
|
def to_date(value, options: [])
|
27
|
-
value
|
27
|
+
value.to_date.to_s if value
|
28
28
|
end
|
29
29
|
|
30
30
|
def upcase(value, options: [])
|
data/lib/tiny_admin/utils.rb
CHANGED
@@ -5,7 +5,8 @@ module TinyAdmin
|
|
5
5
|
def params_to_s(params)
|
6
6
|
list = params.each_with_object([]) do |(param, value), result|
|
7
7
|
if value.is_a?(Hash)
|
8
|
-
|
8
|
+
values = value.map { |key, val| "#{param}[#{key}]=#{val}" }
|
9
|
+
result.concat(values)
|
9
10
|
else
|
10
11
|
result.push(["#{param}=#{value}"])
|
11
12
|
end
|
@@ -18,13 +19,13 @@ module TinyAdmin
|
|
18
19
|
page.options = options
|
19
20
|
page.head_component = settings.components[:head]&.new
|
20
21
|
page.flash_component = settings.components[:flash]&.new
|
21
|
-
page.navbar_component = settings.components[:navbar]&.new
|
22
|
+
page.navbar_component = settings.components[:navbar]&.new
|
23
|
+
page.navbar_component&.update_attributes(
|
22
24
|
current_slug: context&.slug,
|
23
25
|
root_path: settings.root_path,
|
24
26
|
root_title: settings.root[:title],
|
25
|
-
items: options&.include?(:no_menu) ? [] :
|
27
|
+
items: options&.include?(:no_menu) ? [] : context&.navbar
|
26
28
|
)
|
27
|
-
|
28
29
|
yield(page) if block_given?
|
29
30
|
end
|
30
31
|
end
|
@@ -36,6 +37,12 @@ module TinyAdmin
|
|
36
37
|
route[0] == '/' ? route : route.prepend('/')
|
37
38
|
end
|
38
39
|
|
40
|
+
def to_label(string)
|
41
|
+
return '' unless string
|
42
|
+
|
43
|
+
string.respond_to?(:humanize) ? string.humanize : string.tr('_', ' ').capitalize
|
44
|
+
end
|
45
|
+
|
39
46
|
def context
|
40
47
|
TinyAdmin::Context.instance
|
41
48
|
end
|
data/lib/tiny_admin/version.rb
CHANGED
@@ -4,7 +4,7 @@ module TinyAdmin
|
|
4
4
|
module Views
|
5
5
|
module Actions
|
6
6
|
class Index < DefaultLayout
|
7
|
-
attr_accessor :actions, :fields, :filters, :pagination_component, :prepare_record, :records
|
7
|
+
attr_accessor :actions, :fields, :filters, :links, :pagination_component, :prepare_record, :records
|
8
8
|
|
9
9
|
def template
|
10
10
|
super do
|
@@ -30,8 +30,9 @@ module TinyAdmin
|
|
30
30
|
|
31
31
|
if filters&.any?
|
32
32
|
div(class: 'col-3') {
|
33
|
-
|
34
|
-
|
33
|
+
filters_form = TinyAdmin::Views::Components::FiltersForm.new
|
34
|
+
filters_form.update_attributes(section_path: route_for(context.slug), filters: filters)
|
35
|
+
render filters_form
|
35
36
|
}
|
36
37
|
end
|
37
38
|
}
|
@@ -65,16 +66,33 @@ module TinyAdmin
|
|
65
66
|
field = fields[key]
|
66
67
|
td(class: "field-value-#{field.name} field-value-type-#{field.type}") {
|
67
68
|
if field.options && field.options[:link_to]
|
68
|
-
|
69
|
-
|
70
|
-
|
69
|
+
a(href: route_for(field.options[:link_to], reference: value)) {
|
70
|
+
field.apply_call_option(record) || value
|
71
|
+
}
|
71
72
|
else
|
72
73
|
value
|
73
74
|
end
|
74
75
|
}
|
75
76
|
end
|
76
|
-
|
77
|
-
|
77
|
+
|
78
|
+
td(class: 'actions p-1') {
|
79
|
+
div(class: 'btn-group btn-group-sm') {
|
80
|
+
link_class = 'btn btn-outline-secondary'
|
81
|
+
if links
|
82
|
+
links.each do |link|
|
83
|
+
whitespace
|
84
|
+
if link == 'show'
|
85
|
+
a(href: route_for(context.slug, reference: record.id), class: link_class) { 'show' }
|
86
|
+
else
|
87
|
+
a(href: route_for(context.slug, reference: record.id, action: link), class: link_class) {
|
88
|
+
to_label(link)
|
89
|
+
}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
else
|
93
|
+
a(href: route_for(context.slug, reference: record.id), class: link_class) { 'show' }
|
94
|
+
end
|
95
|
+
}
|
78
96
|
}
|
79
97
|
}
|
80
98
|
end
|
@@ -86,8 +104,9 @@ module TinyAdmin
|
|
86
104
|
(actions || {}).each do |action, action_class|
|
87
105
|
li(class: 'nav-item mx-1') {
|
88
106
|
href = route_for(context.slug, action: action)
|
89
|
-
|
90
|
-
|
107
|
+
a(href: href, class: 'nav-link btn btn-outline-secondary') {
|
108
|
+
action_class.respond_to?(:title) ? action_class.title : action
|
109
|
+
}
|
91
110
|
}
|
92
111
|
end
|
93
112
|
}
|
@@ -25,10 +25,10 @@ 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
|
-
if field.options
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
if field.options[:link_to]
|
29
|
+
a(href: route_for(field.options[:link_to], reference: value)) {
|
30
|
+
field.apply_call_option(record) || value
|
31
|
+
}
|
32
32
|
else
|
33
33
|
value
|
34
34
|
end
|
@@ -46,8 +46,9 @@ module TinyAdmin
|
|
46
46
|
(actions || {}).each do |action, action_class|
|
47
47
|
li(class: 'nav-item mx-1') {
|
48
48
|
href = route_for(context.slug, reference: context.reference, action: action)
|
49
|
-
|
50
|
-
|
49
|
+
a(href: href, class: 'nav-link btn btn-outline-secondary') {
|
50
|
+
action_class.respond_to?(:title) ? action_class.title : action
|
51
|
+
}
|
51
52
|
}
|
52
53
|
end
|
53
54
|
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TinyAdmin
|
4
|
+
module Views
|
5
|
+
module Components
|
6
|
+
class BasicComponent < Phlex::HTML
|
7
|
+
def update_attributes(attributes)
|
8
|
+
attributes.each do |key, value|
|
9
|
+
send("#{key}=", value)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -3,13 +3,8 @@
|
|
3
3
|
module TinyAdmin
|
4
4
|
module Views
|
5
5
|
module Components
|
6
|
-
class FiltersForm <
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(section_path:, filters:)
|
10
|
-
@section_path = section_path
|
11
|
-
@filters = filters
|
12
|
-
end
|
6
|
+
class FiltersForm < BasicComponent
|
7
|
+
attr_accessor :filters, :section_path
|
13
8
|
|
14
9
|
def template
|
15
10
|
form(class: 'form_filters', method: 'get') {
|
@@ -3,24 +3,21 @@
|
|
3
3
|
module TinyAdmin
|
4
4
|
module Views
|
5
5
|
module Components
|
6
|
-
class Flash <
|
7
|
-
|
6
|
+
class Flash < BasicComponent
|
7
|
+
attr_accessor :messages
|
8
8
|
|
9
9
|
def template
|
10
|
+
@messages ||= {}
|
11
|
+
notices = messages[:notices]
|
12
|
+
warnings = messages[:warnings]
|
13
|
+
errors = messages[:errors]
|
14
|
+
|
10
15
|
div(class: 'flash') {
|
11
16
|
div(class: 'notices alert alert-success', role: 'alert') { notices.join(', ') } if notices&.any?
|
12
17
|
div(class: 'notices alert alert-warning', role: 'alert') { warnings.join(', ') } if warnings&.any?
|
13
18
|
div(class: 'notices alert alert-danger', role: 'alert') { errors.join(', ') } if errors&.any?
|
14
19
|
}
|
15
20
|
end
|
16
|
-
|
17
|
-
def update(messages:)
|
18
|
-
return unless messages
|
19
|
-
|
20
|
-
@notices = messages[:notices]
|
21
|
-
@warnings = messages[:warnings]
|
22
|
-
@errors = messages[:errors]
|
23
|
-
end
|
24
21
|
end
|
25
22
|
end
|
26
23
|
end
|
@@ -3,8 +3,8 @@
|
|
3
3
|
module TinyAdmin
|
4
4
|
module Views
|
5
5
|
module Components
|
6
|
-
class Head <
|
7
|
-
|
6
|
+
class Head < BasicComponent
|
7
|
+
attr_accessor :extra_styles, :page_title, :style_links
|
8
8
|
|
9
9
|
def template
|
10
10
|
head {
|
@@ -19,12 +19,6 @@ module TinyAdmin
|
|
19
19
|
style { extra_styles } if extra_styles
|
20
20
|
}
|
21
21
|
end
|
22
|
-
|
23
|
-
def update(page_title, style_links: [], extra_styles: nil)
|
24
|
-
@page_title = page_title
|
25
|
-
@style_links = style_links
|
26
|
-
@extra_styles = extra_styles
|
27
|
-
end
|
28
22
|
end
|
29
23
|
end
|
30
24
|
end
|
@@ -3,33 +3,34 @@
|
|
3
3
|
module TinyAdmin
|
4
4
|
module Views
|
5
5
|
module Components
|
6
|
-
class Navbar <
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(root_path:, root_title:, items: [], current_slug: nil)
|
10
|
-
@root_path = root_path
|
11
|
-
@root_title = root_title
|
12
|
-
@items = items || []
|
13
|
-
@current_slug = current_slug
|
14
|
-
end
|
6
|
+
class Navbar < BasicComponent
|
7
|
+
attr_accessor :current_slug, :items, :root_path, :root_title
|
15
8
|
|
16
9
|
def template
|
17
10
|
nav(class: 'navbar navbar-expand-lg') {
|
18
11
|
div(class: 'container') {
|
19
12
|
a(class: 'navbar-brand', href: root_path) { root_title }
|
20
|
-
button(
|
13
|
+
button(
|
14
|
+
class: 'navbar-toggler',
|
15
|
+
type: 'button',
|
16
|
+
'data-bs-toggle' => 'collapse',
|
17
|
+
'data-bs-target' => '#navbarNav',
|
18
|
+
'aria-controls' => 'navbarNav',
|
19
|
+
'aria-expanded' => 'false',
|
20
|
+
'aria-label' => 'Toggle navigation'
|
21
|
+
) {
|
21
22
|
span(class: 'navbar-toggler-icon')
|
22
23
|
}
|
23
24
|
div(class: 'collapse navbar-collapse', id: 'navbarNav') {
|
24
25
|
ul(class: 'navbar-nav') {
|
25
|
-
items.each do |slug,
|
26
|
+
items.each do |slug, item|
|
26
27
|
classes = %w[nav-link]
|
27
28
|
classes << 'active' if slug == current_slug
|
28
|
-
link_attributes = { class: classes.join(' '), href: path, 'aria-current' => 'page' }
|
29
|
-
link_attributes.merge!(options) if options
|
29
|
+
link_attributes = { class: classes.join(' '), href: item[:path], 'aria-current' => 'page' }
|
30
|
+
link_attributes.merge!(item[:options]) if item[:options]
|
30
31
|
|
31
32
|
li(class: 'nav-item') {
|
32
|
-
a(**link_attributes) { name }
|
33
|
+
a(**link_attributes) { item[:name] }
|
33
34
|
}
|
34
35
|
end
|
35
36
|
}
|
@@ -3,8 +3,8 @@
|
|
3
3
|
module TinyAdmin
|
4
4
|
module Views
|
5
5
|
module Components
|
6
|
-
class Pagination <
|
7
|
-
|
6
|
+
class Pagination < BasicComponent
|
7
|
+
attr_accessor :current, :pages, :query_string
|
8
8
|
|
9
9
|
def template
|
10
10
|
div(class: 'pagination-div') {
|
@@ -25,12 +25,6 @@ module TinyAdmin
|
|
25
25
|
}
|
26
26
|
end
|
27
27
|
|
28
|
-
def update(current:, pages:, query_string:)
|
29
|
-
@current = current
|
30
|
-
@pages = pages
|
31
|
-
@query_string = query_string
|
32
|
-
end
|
33
|
-
|
34
28
|
private
|
35
29
|
|
36
30
|
def pages_range(range, with_dots: false)
|
@@ -6,8 +6,9 @@ module TinyAdmin
|
|
6
6
|
attr_accessor :flash_component, :head_component, :messages, :navbar_component, :options, :title
|
7
7
|
|
8
8
|
def template(&block)
|
9
|
-
|
10
|
-
|
9
|
+
extra_styles = settings.extra_styles
|
10
|
+
flash_component&.messages = messages
|
11
|
+
head_component&.update_attributes(page_title: title, style_links: style_links, extra_styles: extra_styles)
|
11
12
|
|
12
13
|
doctype
|
13
14
|
html {
|
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.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mattia Roccoberton
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-04-
|
11
|
+
date: 2023-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: phlex
|
@@ -94,6 +94,7 @@ files:
|
|
94
94
|
- lib/tiny_admin/views/actions/index.rb
|
95
95
|
- lib/tiny_admin/views/actions/show.rb
|
96
96
|
- lib/tiny_admin/views/basic_layout.rb
|
97
|
+
- lib/tiny_admin/views/components/basic_component.rb
|
97
98
|
- lib/tiny_admin/views/components/filters_form.rb
|
98
99
|
- lib/tiny_admin/views/components/flash.rb
|
99
100
|
- lib/tiny_admin/views/components/head.rb
|