tiny_admin 0.4.0 → 0.6.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 +140 -44
- data/lib/tiny_admin/actions/index.rb +8 -5
- data/lib/tiny_admin/actions/show.rb +5 -6
- data/lib/tiny_admin/authentication.rb +3 -3
- data/lib/tiny_admin/basic_app.rb +3 -3
- 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 +10 -6
- data/lib/tiny_admin/plugins/base_repository.rb +1 -1
- data/lib/tiny_admin/router.rb +22 -18
- data/lib/tiny_admin/settings.rb +103 -50
- data/lib/tiny_admin/support.rb +4 -4
- data/lib/tiny_admin/utils.rb +16 -13
- 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/basic_layout.rb +2 -0
- 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 +5 -4
- data/lib/tiny_admin/views/pages/content.rb +17 -0
- data/lib/tiny_admin.rb +8 -3
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0dfe9b2fef29073201e228daf3a7d89003902f8d98108e58cb8245fdb9a533e3
|
4
|
+
data.tar.gz: 6634891a9bbc2585549a759cd061a25234b834ebdcc0b77fbbb9bd2297b0a626
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78b49e3f8b7efe941e24734692db4e4baaa2159f58a10f49ce0563f8c4cfa5950f3e155184016faff12c415ff1a6632d4ee06cd61659a466d170d4b7954becf7
|
7
|
+
data.tar.gz: 16c4fe68dca49b84c4c8ca834c9e3c2ce2304f5a78dc378cfdde837462a96a4e00030e5098083d025ad9ab28060ea2aa581725434ffeec1582715e5fa725ff28
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Tiny Admin
|
2
2
|
|
3
|
-
[](https://badge.fury.io/rb/tiny_admin) [](https://github.com/blocknotes/tiny_admin/actions/workflows/linters.yml) [](https://badge.fury.io/rb/tiny_admin) [](https://github.com/blocknotes/tiny_admin/actions/workflows/linters.yml) [](https://github.com/blocknotes/tiny_admin/actions/workflows/specs.yml)
|
4
4
|
|
5
5
|
A compact and composable dashboard component for Ruby.
|
6
6
|
|
@@ -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.6'`
|
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,38 @@ 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
|
+
- **Content**: define how to present page with inline content;
|
55
|
+
- **PageNotFound**: define how to present pages not found;
|
56
|
+
- **RecordNotFound**: define how to present record not found page;
|
57
|
+
- **SimpleAuthLogin**: define how to present the login form for SimpleAuth plugin;
|
58
|
+
- **Index**: define how to present a collection of items;
|
59
|
+
- **Show**: define how to present the details of an item.
|
55
60
|
|
56
61
|
### View components
|
57
62
|
|
58
63
|
Components available:
|
59
|
-
|
60
|
-
-
|
61
|
-
-
|
62
|
-
-
|
63
|
-
-
|
64
|
+
|
65
|
+
- **FiltersForm**: define how to present the filters form in the resource collection pages;
|
66
|
+
- **Flash**: define how to present the flash messages;
|
67
|
+
- **Head**: define how to present the Head tag;
|
68
|
+
- **Navbar**: define how to present the navbar (the default one uses the Bootstrap structure);
|
69
|
+
- **Pagination**: define how to present the pagination of a collection.
|
64
70
|
|
65
71
|
## Configuration
|
66
72
|
|
@@ -70,9 +76,10 @@ See [extra](extra) folder for some usage examples.
|
|
70
76
|
The following options are supported:
|
71
77
|
|
72
78
|
`root` (Hash): define the root section of the admin, properties:
|
73
|
-
|
74
|
-
|
75
|
-
|
79
|
+
|
80
|
+
- `title` (String): root section's title;
|
81
|
+
- `page` (String): a view object to render;
|
82
|
+
- `redirect` (String): alternative to _page_ option - redirects to a specific slug;
|
76
83
|
|
77
84
|
Example:
|
78
85
|
|
@@ -88,9 +95,21 @@ root:
|
|
88
95
|
|
89
96
|
`record_not_found` (String): a view object to render when a missing record is requested.
|
90
97
|
|
98
|
+
`style_links` (Array of hashes): list of styles files to include, properties:
|
99
|
+
|
100
|
+
- `href` (String): URL for the style file;
|
101
|
+
- `rel` (String): type of style file.
|
102
|
+
|
103
|
+
`scripts` (Array of hashes): list of scripts to include, properties:
|
104
|
+
|
105
|
+
- `src` (String): source URL for the script.
|
106
|
+
|
107
|
+
`extra_styles` (String): inline CSS styles.
|
108
|
+
|
91
109
|
`authentication` (Hash): define the authentication method, properties:
|
92
|
-
|
93
|
-
|
110
|
+
|
111
|
+
- `plugin` (String): a plugin class to use (ex. `TinyAdmin::Plugins::SimpleAuth`);
|
112
|
+
- `password` (String): a password hash used by _SimpleAuth_ plugin (generated with `Digest::SHA512.hexdigest("some password")`).
|
94
113
|
|
95
114
|
Example:
|
96
115
|
|
@@ -101,15 +120,32 @@ authentication:
|
|
101
120
|
```
|
102
121
|
|
103
122
|
`sections` (Array of hashes): define the admin sections, properties:
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
123
|
+
|
124
|
+
- `slug` (String): section reference identifier;
|
125
|
+
- `name` (String): section's title;
|
126
|
+
- `type` (String): the type of section: `content`, `page`, `resource` or `url`;
|
127
|
+
- other properties depends on the section's type.
|
128
|
+
|
129
|
+
For _content_ sections:
|
130
|
+
|
131
|
+
- `content` (String): the HTML content to present.
|
132
|
+
|
133
|
+
Example:
|
134
|
+
|
135
|
+
```yml
|
136
|
+
slug: test-content
|
137
|
+
name: Test content
|
138
|
+
type: content
|
139
|
+
content: >
|
140
|
+
<h1>Test content!</h1>
|
141
|
+
<p>Some test content</p>
|
142
|
+
```
|
108
143
|
|
109
144
|
For _url_ sections:
|
110
|
-
|
111
|
-
|
112
|
-
|
145
|
+
|
146
|
+
- `url` (String): the URL to load when clicking on the section's menu item;
|
147
|
+
- `options` (Hash): properties:
|
148
|
+
+ `target` (String): link _target_ attributes (ex. `_blank`).
|
113
149
|
|
114
150
|
Example:
|
115
151
|
|
@@ -123,7 +159,8 @@ options:
|
|
123
159
|
```
|
124
160
|
|
125
161
|
For _page_ sections:
|
126
|
-
|
162
|
+
|
163
|
+
- `page` (String): a view object to render.
|
127
164
|
|
128
165
|
Example:
|
129
166
|
|
@@ -135,14 +172,15 @@ page: Admin::Stats
|
|
135
172
|
```
|
136
173
|
|
137
174
|
For _resource_ sections:
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
175
|
+
|
176
|
+
- `model` (String): the class to use to fetch the data on an item of a collection;
|
177
|
+
- `repository` (String): the class to get the properties related to the model;
|
178
|
+
- `index` (Hash): collection's action options (see below);
|
179
|
+
- `show` (Hash): detail's action options (see below);
|
180
|
+
- `collection_actions` (Array of hashes): custom collection's actions;
|
181
|
+
- `member_actions` (Array of hashes): custom details's actions;
|
182
|
+
- `only` (Array of strings): list of supported actions (ex. `index`);
|
183
|
+
- `options` (Array of strings): resource options (ex. `hidden`).
|
146
184
|
|
147
185
|
Example:
|
148
186
|
|
@@ -153,14 +191,72 @@ type: resource
|
|
153
191
|
model: Post
|
154
192
|
```
|
155
193
|
|
156
|
-
|
157
|
-
- `href` (String): URL for the style file;
|
158
|
-
- `rel` (String): type of style file.
|
194
|
+
#### Resource index options
|
159
195
|
|
160
|
-
|
161
|
-
- `src` (String): source URL for the script.
|
196
|
+
The Index hash supports the following options:
|
162
197
|
|
163
|
-
`
|
198
|
+
- `attributes` (Array): fields to expose in the resource list page;
|
199
|
+
- `filters` (Array): filter the current listing;
|
200
|
+
- `links` (Array): custom member actions to expose for each list's entry (defined in _member_actions_);
|
201
|
+
- `pagination` (Integer): max pages size;
|
202
|
+
- `sort` (Array): sort options to pass to the listing query.
|
203
|
+
|
204
|
+
Example:
|
205
|
+
|
206
|
+
```yml
|
207
|
+
index:
|
208
|
+
sort:
|
209
|
+
- id DESC
|
210
|
+
pagination: 10
|
211
|
+
attributes:
|
212
|
+
- id
|
213
|
+
- author: call, name
|
214
|
+
- position: round, 1
|
215
|
+
- field: author_id
|
216
|
+
header: The author
|
217
|
+
link_to: authors
|
218
|
+
call: author, name
|
219
|
+
filters:
|
220
|
+
- title
|
221
|
+
- field: state
|
222
|
+
type: select
|
223
|
+
values:
|
224
|
+
- published
|
225
|
+
- draft
|
226
|
+
- archived
|
227
|
+
links:
|
228
|
+
- show
|
229
|
+
- author_posts
|
230
|
+
- csv_export
|
231
|
+
```
|
232
|
+
|
233
|
+
#### Resource show options
|
234
|
+
|
235
|
+
The Show hash supports the following options:
|
236
|
+
|
237
|
+
- `attributes` (Array): fields to expose in the resource details page.
|
238
|
+
|
239
|
+
Example:
|
240
|
+
|
241
|
+
```yml
|
242
|
+
show:
|
243
|
+
attributes:
|
244
|
+
# Expose the id column
|
245
|
+
- id
|
246
|
+
# Expose the title column, calling `downcase` support method
|
247
|
+
- title: downcase
|
248
|
+
# Expose the category column, calling `upcase` support method
|
249
|
+
- category: upcase
|
250
|
+
# Expose the position column, calling `format` support method with argument %f
|
251
|
+
- position: format, %f
|
252
|
+
# Expose the position created_at, calling `strftime` support method with argument %Y%m%d %H:%M
|
253
|
+
- created_at: strftime, %Y%m%d %H:%M
|
254
|
+
# Expose the author_id column, with a custom header label, linked to authors section and calling author.name to get the value
|
255
|
+
- field: author_id
|
256
|
+
header: The author
|
257
|
+
link_to: authors
|
258
|
+
call: author, name
|
259
|
+
```
|
164
260
|
|
165
261
|
### Sample
|
166
262
|
|
@@ -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,18 +14,19 @@ 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)
|
20
21
|
records, total_count = repository.list(page: current_page, limit: pagination, filters: filters, sort: sort)
|
21
22
|
|
22
23
|
prepare_page(Views::Actions::Index) do |page|
|
23
|
-
setup_pagination(page, settings.components[:pagination], total_count: total_count)
|
24
|
+
setup_pagination(page, TinyAdmin.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
|
)
|
@@ -5,7 +5,7 @@ module TinyAdmin
|
|
5
5
|
route do |r|
|
6
6
|
r.get 'unauthenticated' do
|
7
7
|
if current_user
|
8
|
-
r.redirect settings.root_path
|
8
|
+
r.redirect TinyAdmin.settings.root_path
|
9
9
|
else
|
10
10
|
render_login
|
11
11
|
end
|
@@ -17,14 +17,14 @@ module TinyAdmin
|
|
17
17
|
|
18
18
|
r.get 'logout' do
|
19
19
|
logout_user
|
20
|
-
r.redirect settings.root_path
|
20
|
+
r.redirect TinyAdmin.settings.root_path
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
private
|
25
25
|
|
26
26
|
def render_login(notices: nil, warnings: nil, errors: nil)
|
27
|
-
page = prepare_page(settings.authentication[:login], options: %i[no_menu compact_layout])
|
27
|
+
page = prepare_page(TinyAdmin.settings.authentication[:login], options: %i[no_menu compact_layout])
|
28
28
|
page.messages = {
|
29
29
|
notices: notices || flash['notices'],
|
30
30
|
warnings: warnings || flash['warnings'],
|
data/lib/tiny_admin/basic_app.rb
CHANGED
@@ -6,7 +6,7 @@ module TinyAdmin
|
|
6
6
|
|
7
7
|
class << self
|
8
8
|
def authentication_plugin
|
9
|
-
plugin = TinyAdmin
|
9
|
+
plugin = TinyAdmin.settings.authentication&.dig(:plugin)
|
10
10
|
plugin_class = plugin.is_a?(String) ? Object.const_get(plugin) : plugin
|
11
11
|
plugin_class || TinyAdmin::Plugins::NoAuth
|
12
12
|
end
|
@@ -17,8 +17,8 @@ module TinyAdmin
|
|
17
17
|
plugin :render, engine: 'html'
|
18
18
|
plugin :sessions, secret: SecureRandom.hex(64)
|
19
19
|
|
20
|
-
plugin authentication_plugin, TinyAdmin
|
20
|
+
plugin authentication_plugin, TinyAdmin.settings.authentication
|
21
21
|
|
22
|
-
not_found { prepare_page(settings.page_not_found).call }
|
22
|
+
not_found { prepare_page(TinyAdmin.settings.page_not_found).call }
|
23
23
|
end
|
24
24
|
end
|
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
|
@@ -20,12 +20,12 @@ module TinyAdmin
|
|
20
20
|
def fields(options: nil)
|
21
21
|
if options
|
22
22
|
types = model.columns.to_h { [_1.name, _1.type] }
|
23
|
-
options.
|
24
|
-
|
23
|
+
options.to_h do |name, field_options|
|
24
|
+
[name, TinyAdmin::Field.create_field(name: name, type: types[name], options: field_options)]
|
25
25
|
end
|
26
26
|
else
|
27
|
-
model.columns.
|
28
|
-
|
27
|
+
model.columns.to_h do |column|
|
28
|
+
[column.name, TinyAdmin::Field.create_field(name: column.name, type: column.type)]
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -42,8 +42,12 @@ module TinyAdmin
|
|
42
42
|
raise BaseRepository::RecordNotFound, e.message
|
43
43
|
end
|
44
44
|
|
45
|
-
def
|
46
|
-
|
45
|
+
def collection
|
46
|
+
model.all
|
47
|
+
end
|
48
|
+
|
49
|
+
def list(page: 1, limit: 10, sort: nil, filters: nil)
|
50
|
+
query = sort ? collection.order(sort) : collection
|
47
51
|
query = apply_filters(query, filters) if filters
|
48
52
|
page_offset = page.positive? ? (page - 1) * limit : 0
|
49
53
|
records = query.offset(page_offset).limit(limit).to_a
|
@@ -18,7 +18,7 @@ module TinyAdmin
|
|
18
18
|
converter = Object.const_get(field[:converter])
|
19
19
|
converter.send(method, value, options: options || [])
|
20
20
|
else
|
21
|
-
|
21
|
+
TinyAdmin.settings.helper_class.send(method, value, options: options || [])
|
22
22
|
end
|
23
23
|
else
|
24
24
|
value&.to_s
|
data/lib/tiny_admin/router.rb
CHANGED
@@ -2,10 +2,11 @@
|
|
2
2
|
|
3
3
|
module TinyAdmin
|
4
4
|
class Router < BasicApp
|
5
|
+
TinyAdmin.settings.load_settings
|
6
|
+
|
5
7
|
route do |r|
|
6
|
-
context.settings = TinyAdmin::Settings.instance
|
7
|
-
context.settings.load_settings
|
8
8
|
context.router = r
|
9
|
+
context.settings = TinyAdmin.settings
|
9
10
|
|
10
11
|
r.on 'auth' do
|
11
12
|
context.slug = nil
|
@@ -26,14 +27,14 @@ module TinyAdmin
|
|
26
27
|
|
27
28
|
r.post '' do
|
28
29
|
context.slug = nil
|
29
|
-
r.redirect settings.root_path
|
30
|
+
r.redirect TinyAdmin.settings.root_path
|
30
31
|
end
|
31
32
|
|
32
|
-
context.
|
33
|
-
setup_page_route(r, slug,
|
33
|
+
context.pages.each do |slug, page_data|
|
34
|
+
setup_page_route(r, slug, page_data)
|
34
35
|
end
|
35
36
|
|
36
|
-
context.
|
37
|
+
context.resources.each do |slug, options|
|
37
38
|
setup_resource_routes(r, slug, options: options || {})
|
38
39
|
end
|
39
40
|
|
@@ -51,19 +52,21 @@ module TinyAdmin
|
|
51
52
|
|
52
53
|
def root_route(router)
|
53
54
|
context.slug = nil
|
54
|
-
if settings.root[:redirect]
|
55
|
-
router.redirect route_for(settings.root[:redirect])
|
55
|
+
if TinyAdmin.settings.root[:redirect]
|
56
|
+
router.redirect route_for(TinyAdmin.settings.root[:redirect])
|
56
57
|
else
|
57
|
-
page = settings.root[:page]
|
58
|
+
page = TinyAdmin.settings.root[:page]
|
58
59
|
page_class = page.is_a?(String) ? Object.const_get(page) : page
|
59
60
|
render_page prepare_page(page_class)
|
60
61
|
end
|
61
62
|
end
|
62
63
|
|
63
|
-
def setup_page_route(router, slug,
|
64
|
+
def setup_page_route(router, slug, page_data)
|
64
65
|
router.get slug do
|
65
66
|
context.slug = slug
|
66
|
-
|
67
|
+
page = prepare_page(page_data[:class])
|
68
|
+
page.update_attributes(content: page_data[:content]) if page_data[:content]
|
69
|
+
render_page page
|
67
70
|
end
|
68
71
|
end
|
69
72
|
|
@@ -88,19 +91,19 @@ module TinyAdmin
|
|
88
91
|
)
|
89
92
|
|
90
93
|
# Index
|
91
|
-
|
92
|
-
if !actions || actions.include?(:index) || actions.include?('index')
|
94
|
+
if options[:only].include?(:index) || options[:only].include?('index')
|
93
95
|
router.is do
|
96
|
+
context.actions = custom_actions
|
94
97
|
context.request = request
|
95
98
|
index_action = TinyAdmin::Actions::Index.new
|
96
|
-
render_page index_action.call(app: self, context: context, options: action_options
|
99
|
+
render_page index_action.call(app: self, context: context, options: action_options)
|
97
100
|
end
|
98
101
|
end
|
99
102
|
end
|
100
103
|
|
101
104
|
def setup_member_routes(router, options:)
|
102
105
|
context.repository = options[:repository].new(options[:model])
|
103
|
-
action_options = (options[:show] || {}).merge(record_not_found_page: settings.record_not_found)
|
106
|
+
action_options = (options[:show] || {}).merge(record_not_found_page: TinyAdmin.settings.record_not_found)
|
104
107
|
|
105
108
|
router.on String do |reference|
|
106
109
|
context.reference = reference
|
@@ -114,12 +117,12 @@ module TinyAdmin
|
|
114
117
|
)
|
115
118
|
|
116
119
|
# Show
|
117
|
-
|
118
|
-
if !actions || actions.include?(:show) || actions.include?('show')
|
120
|
+
if options[:only].include?(:show) || options[:only].include?('show')
|
119
121
|
router.is do
|
122
|
+
context.actions = custom_actions
|
120
123
|
context.request = request
|
121
124
|
show_action = TinyAdmin::Actions::Show.new
|
122
|
-
render_page show_action.call(app: self, context: context, options: action_options
|
125
|
+
render_page show_action.call(app: self, context: context, options: action_options)
|
123
126
|
end
|
124
127
|
end
|
125
128
|
end
|
@@ -132,6 +135,7 @@ module TinyAdmin
|
|
132
135
|
action_class = action.is_a?(String) ? Object.const_get(action) : action
|
133
136
|
|
134
137
|
router.get action_slug.to_s do
|
138
|
+
context.actions = {}
|
135
139
|
context.request = request
|
136
140
|
custom_action = action_class.new
|
137
141
|
render_page custom_action.call(app: self, context: context, options: options)
|
data/lib/tiny_admin/settings.rb
CHANGED
@@ -12,6 +12,7 @@ module TinyAdmin
|
|
12
12
|
%i[components head] => Views::Components::Head,
|
13
13
|
%i[components navbar] => Views::Components::Navbar,
|
14
14
|
%i[components pagination] => Views::Components::Pagination,
|
15
|
+
%i[content_page] => Views::Pages::Content,
|
15
16
|
%i[helper_class] => Support,
|
16
17
|
%i[page_not_found] => Views::Pages::PageNotFound,
|
17
18
|
%i[record_not_found] => Views::Pages::RecordNotFound,
|
@@ -21,28 +22,40 @@ module TinyAdmin
|
|
21
22
|
%i[root title] => 'TinyAdmin'
|
22
23
|
}.freeze
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
25
|
+
OPTIONS = %i[
|
26
|
+
authentication
|
27
|
+
components
|
28
|
+
content_page
|
29
|
+
extra_styles
|
30
|
+
helper_class
|
31
|
+
page_not_found
|
32
|
+
record_not_found
|
33
|
+
repository
|
34
|
+
root
|
35
|
+
root_path
|
36
|
+
sections
|
37
|
+
scripts
|
38
|
+
style_links
|
39
|
+
].freeze
|
40
|
+
|
41
|
+
OPTIONS.each do |option|
|
42
|
+
define_method(option) do
|
43
|
+
self[option]
|
44
|
+
end
|
45
|
+
|
46
|
+
define_method("#{option}=") do |value|
|
47
|
+
self[option] = value
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def [](*path)
|
52
|
+
key, option = fetch_setting(path)
|
53
|
+
option[key]
|
42
54
|
end
|
43
55
|
|
44
|
-
def []=(
|
45
|
-
|
56
|
+
def []=(*path, value)
|
57
|
+
key, option = fetch_setting(path)
|
58
|
+
option[key] = value
|
46
59
|
convert_value(key, value)
|
47
60
|
end
|
48
61
|
|
@@ -57,43 +70,29 @@ module TinyAdmin
|
|
57
70
|
end
|
58
71
|
end
|
59
72
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
73
|
+
context.pages ||= {}
|
74
|
+
context.resources ||= {}
|
75
|
+
self.sections ||= []
|
76
|
+
self.root_path = '/' if root_path == ''
|
77
|
+
|
78
|
+
if authentication[:plugin] <= Plugins::SimpleAuth
|
79
|
+
authentication[:logout] ||= { name: 'logout', path: "#{root_path}/auth/logout" }
|
66
80
|
end
|
67
|
-
|
81
|
+
context.navbar = prepare_navbar(sections, logout: authentication[:logout])
|
68
82
|
end
|
69
83
|
|
70
|
-
def
|
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
|
90
|
-
end
|
91
|
-
items['auth/logout'] = logout if logout
|
92
|
-
items
|
84
|
+
def reset!
|
85
|
+
@options = {}
|
93
86
|
end
|
94
87
|
|
95
88
|
private
|
96
89
|
|
90
|
+
def fetch_setting(path)
|
91
|
+
@options ||= {}
|
92
|
+
*parts, last = path.map(&:to_sym)
|
93
|
+
[last, parts.inject(@options) { |result, part| result[part] ||= {} }]
|
94
|
+
end
|
95
|
+
|
97
96
|
def convert_value(key, value)
|
98
97
|
if value.is_a?(Hash)
|
99
98
|
value.each_key do |key2|
|
@@ -106,5 +105,59 @@ module TinyAdmin
|
|
106
105
|
self[key] = Object.const_get(self[key])
|
107
106
|
end
|
108
107
|
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
|
109
162
|
end
|
110
163
|
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
|
@@ -16,32 +17,34 @@ module TinyAdmin
|
|
16
17
|
def prepare_page(page_class, options: nil)
|
17
18
|
page_class.new.tap do |page|
|
18
19
|
page.options = options
|
19
|
-
page.head_component = settings.components[:head]&.new
|
20
|
-
page.flash_component = settings.components[:flash]&.new
|
21
|
-
page.navbar_component = settings.components[:navbar]&.new
|
20
|
+
page.head_component = TinyAdmin.settings.components[:head]&.new
|
21
|
+
page.flash_component = TinyAdmin.settings.components[:flash]&.new
|
22
|
+
page.navbar_component = TinyAdmin.settings.components[:navbar]&.new
|
23
|
+
page.navbar_component&.update_attributes(
|
22
24
|
current_slug: context&.slug,
|
23
|
-
root_path: settings.root_path,
|
24
|
-
root_title: settings.root[:title],
|
25
|
-
items: options&.include?(:no_menu) ? [] :
|
25
|
+
root_path: TinyAdmin.settings.root_path,
|
26
|
+
root_title: TinyAdmin.settings.root[:title],
|
27
|
+
items: options&.include?(:no_menu) ? [] : context&.navbar
|
26
28
|
)
|
27
|
-
|
28
29
|
yield(page) if block_given?
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
32
33
|
def route_for(section, reference: nil, action: nil, query: nil)
|
33
|
-
root_path = settings.root_path == '/' ? nil : settings.root_path
|
34
|
+
root_path = TinyAdmin.settings.root_path == '/' ? nil : TinyAdmin.settings.root_path
|
34
35
|
route = [root_path, section, reference, action].compact.join("/")
|
35
36
|
route << "?#{query}" if query
|
36
37
|
route[0] == '/' ? route : route.prepend('/')
|
37
38
|
end
|
38
39
|
|
39
|
-
def
|
40
|
-
|
40
|
+
def to_label(string)
|
41
|
+
return '' unless string
|
42
|
+
|
43
|
+
string.respond_to?(:humanize) ? string.humanize : string.tr('_', ' ').capitalize
|
41
44
|
end
|
42
45
|
|
43
|
-
def
|
44
|
-
TinyAdmin::
|
46
|
+
def context
|
47
|
+
TinyAdmin::Context.instance
|
45
48
|
end
|
46
49
|
end
|
47
50
|
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 = TinyAdmin.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 {
|
@@ -48,7 +49,7 @@ module TinyAdmin
|
|
48
49
|
end
|
49
50
|
|
50
51
|
def style_links
|
51
|
-
settings.style_links || [
|
52
|
+
TinyAdmin.settings.style_links || [
|
52
53
|
# Bootstrap CDN
|
53
54
|
{
|
54
55
|
href: 'https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css',
|
@@ -60,7 +61,7 @@ module TinyAdmin
|
|
60
61
|
end
|
61
62
|
|
62
63
|
def render_scripts
|
63
|
-
(settings.scripts || []).each do |script_attrs|
|
64
|
+
(TinyAdmin.settings.scripts || []).each do |script_attrs|
|
64
65
|
script(**script_attrs)
|
65
66
|
end
|
66
67
|
end
|
data/lib/tiny_admin.rb
CHANGED
@@ -12,15 +12,20 @@ loader.setup
|
|
12
12
|
|
13
13
|
module TinyAdmin
|
14
14
|
def configure(&block)
|
15
|
-
block&.call(
|
15
|
+
block&.call(settings) || settings
|
16
16
|
end
|
17
17
|
|
18
18
|
def configure_from_file(file)
|
19
|
+
settings.reset!
|
19
20
|
config = YAML.load_file(file, symbolize_names: true)
|
20
21
|
config.each do |key, value|
|
21
|
-
|
22
|
+
settings[key] = value
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
25
|
-
|
26
|
+
def settings
|
27
|
+
TinyAdmin::Settings.instance
|
28
|
+
end
|
29
|
+
|
30
|
+
module_function :configure, :configure_from_file, :settings
|
26
31
|
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.6.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-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: phlex
|
@@ -94,12 +94,14 @@ 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
|
100
101
|
- lib/tiny_admin/views/components/navbar.rb
|
101
102
|
- lib/tiny_admin/views/components/pagination.rb
|
102
103
|
- lib/tiny_admin/views/default_layout.rb
|
104
|
+
- lib/tiny_admin/views/pages/content.rb
|
103
105
|
- lib/tiny_admin/views/pages/page_not_found.rb
|
104
106
|
- lib/tiny_admin/views/pages/record_not_found.rb
|
105
107
|
- lib/tiny_admin/views/pages/root.rb
|