rails_material_admin 0.1.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +65 -0
- data/Rakefile +27 -0
- data/app/controllers/concerns/datatable_decorator.rb +96 -0
- data/app/helpers/admin_helper.rb +20 -0
- data/app/helpers/datatables_helper.rb +204 -0
- data/app/views/admin/sessions/new.html.slim +30 -0
- data/app/views/layouts/admin.html.slim +33 -0
- data/app/views/layouts/unauthorized.html.slim +13 -0
- data/app/views/shared/_breadcrumb.html.slim +22 -0
- data/app/views/shared/_error_messages.html.slim +8 -0
- data/app/views/shared/_header.html.slim +24 -0
- data/app/views/shared/_links.html.slim +19 -0
- data/app/views/shared/_radio_box.html.slim +17 -0
- data/app/views/shared/_sidebar.html.slim +23 -0
- data/config/webpack/environment.js +24 -0
- data/lib/generators/crud/crud_generator.rb +194 -0
- data/lib/generators/material_admin/material_admin_generator.rb +259 -0
- data/lib/material_admin.rb +5 -0
- data/lib/material_admin/railtie.rb +4 -0
- data/lib/material_admin/version.rb +3 -0
- data/lib/tasks/material_admin_tasks.rake +4 -0
- metadata +86 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f1478109456a5e1c1d47dcf76c6c71afa8506ec6fdbf6baadd2bf38666ef7788
|
|
4
|
+
data.tar.gz: d840026d251d8f5289ed6269a489be0a1e22c7f49c8a3484499ffc99f70eda3a
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 706bf3b12fbdbec496edfd5fb383ef8811d0073910544234fd9dbeb102ed6b6254bf9f6677be710ca1c7196d32b6561be25630c078cfba78a1b5d29cb3147428
|
|
7
|
+
data.tar.gz: c029ddd8d127f7d336e5ff66ce8381b43257cf31a9b89324394508abd1cd7cb7faba1d06972df88a73a7cc78e30ee58867237a0db370b6480808249f9970ed62
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2020 Tracy Cai
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# MaterialAdmin
|
|
2
|
+
Short description and motivation.
|
|
3
|
+
|
|
4
|
+
## Prerequisite
|
|
5
|
+
### Set up your db first
|
|
6
|
+
|
|
7
|
+
### packsge.json
|
|
8
|
+
```json
|
|
9
|
+
{
|
|
10
|
+
"name": "beebeego-back",
|
|
11
|
+
"private": true,
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@rails/webpacker": "5.1.1",
|
|
14
|
+
"core-js": "3",
|
|
15
|
+
"file-loader": "^6.0.0",
|
|
16
|
+
"url-loader": "^4.1.0",
|
|
17
|
+
"expose-loader": "^0.7.5",
|
|
18
|
+
"resolve-url-loader": "^3.1.1",
|
|
19
|
+
"jquery": "^3.5.1",
|
|
20
|
+
"select2": "^4.0.13",
|
|
21
|
+
"popper.js": "^1.16.1",
|
|
22
|
+
"rails-ujs": "^5.2.4-2",
|
|
23
|
+
"stimulus": "^1.1.1",
|
|
24
|
+
"turbolinks": "^5.2.0",
|
|
25
|
+
"datatables.net-bs4": "^1.10.21",
|
|
26
|
+
"datatables.net-responsive-bs4": "^2.2.5"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"webpack-dev-server": "^3.11.0"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
How to use my plugin.
|
|
37
|
+
|
|
38
|
+
### Installation
|
|
39
|
+
Add this line to your application's Gemfile:
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
gem 'material_admin'
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
And then execute:
|
|
46
|
+
```bash
|
|
47
|
+
$ bundle
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Init an admin template
|
|
51
|
+
```
|
|
52
|
+
rails generate material_admin [layout_name]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Init a simple users CRUD template
|
|
56
|
+
```
|
|
57
|
+
rails generate crud user --options layout_name:[layout_name]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## Contributing
|
|
62
|
+
Contribution directions go here.
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'bundler/setup'
|
|
3
|
+
rescue LoadError
|
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
require 'rdoc/task'
|
|
8
|
+
|
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
11
|
+
rdoc.title = 'MaterialAdmin'
|
|
12
|
+
rdoc.options << '--line-numbers'
|
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
require 'bundler/gem_tasks'
|
|
18
|
+
|
|
19
|
+
require 'rake/testtask'
|
|
20
|
+
|
|
21
|
+
Rake::TestTask.new(:test) do |t|
|
|
22
|
+
t.libs << 'test'
|
|
23
|
+
t.pattern = 'test/**/*_test.rb'
|
|
24
|
+
t.verbose = false
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
task default: :test
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DatatableDecorator
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
def decode_datatable_params
|
|
7
|
+
row_start = params['start'].to_i
|
|
8
|
+
rows_per_page = params['length'].to_i
|
|
9
|
+
search_text = params['search']['value']
|
|
10
|
+
|
|
11
|
+
# Convert hashed arrays into array
|
|
12
|
+
columns = []
|
|
13
|
+
params['columns'].each do |k, v|
|
|
14
|
+
if v['data'].is_a? ActionController::Parameters
|
|
15
|
+
v['data'].each do |kk, vv|
|
|
16
|
+
v['name'] = v['data']['_'] if v['name'].blank?
|
|
17
|
+
v[kk] = vv
|
|
18
|
+
end
|
|
19
|
+
else
|
|
20
|
+
v['name'] = v['data'] if v['name'].blank?
|
|
21
|
+
end
|
|
22
|
+
columns[k.to_i] = v
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
order_columns = []
|
|
26
|
+
include_columns = []
|
|
27
|
+
order_expressions = []
|
|
28
|
+
params['order'].each do |k, v|
|
|
29
|
+
col = columns[v['column'].to_i]
|
|
30
|
+
sort_params = col['sort']
|
|
31
|
+
|
|
32
|
+
if sort_params.present?
|
|
33
|
+
if sort_params.is_a? ActionController::Parameters
|
|
34
|
+
order_expressions << "#{sort_params['model'].pluralize}.#{sort_params['column']} #{v['dir']}"
|
|
35
|
+
include_columns << sort_params['model'].pluralize
|
|
36
|
+
else
|
|
37
|
+
order_expressions << "#{sort_params} #{v['dir']}"
|
|
38
|
+
end
|
|
39
|
+
else
|
|
40
|
+
order_expressions << "#{col['name']} #{v['dir']}"
|
|
41
|
+
end
|
|
42
|
+
order_columns[k.to_i] = v
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Now let's rock it and do the processing
|
|
46
|
+
{
|
|
47
|
+
per_page: rows_per_page,
|
|
48
|
+
page: row_start / rows_per_page + 1,
|
|
49
|
+
columns: columns,
|
|
50
|
+
order_columns: order_columns,
|
|
51
|
+
sort_statement: order_expressions.join(','),
|
|
52
|
+
search_text: search_text,
|
|
53
|
+
include_columns: include_columns
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# 用JSON做一个表示操作成功的Response
|
|
58
|
+
#
|
|
59
|
+
# == Parameters:
|
|
60
|
+
# options:: 选项
|
|
61
|
+
#
|
|
62
|
+
# == Options:
|
|
63
|
+
# 在缺省情况下,调用这个函数给一个空的hash也可以,我会自动给回200为response code,错误信息和对象都为空,但是如果需要的,也可以传入一个带哈希作为选项,主要支持:
|
|
64
|
+
# msg:: 需要显示的信息,缺省为空字符串
|
|
65
|
+
# template:: 需要使用的JSON模板,缺省为common/api
|
|
66
|
+
#
|
|
67
|
+
def json_success_response(options = {})
|
|
68
|
+
template = options.delete(:template) || 'common/api'
|
|
69
|
+
msg = options.delete(:msg) || ''
|
|
70
|
+
@code = 200
|
|
71
|
+
@message = msg
|
|
72
|
+
@errors = nil
|
|
73
|
+
render template, layout: 'json_with_meta'
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# 用JSON做一个表示操作失败的Response
|
|
77
|
+
#
|
|
78
|
+
# == Parameters:
|
|
79
|
+
# msg:: 需要显示的错误信息
|
|
80
|
+
# errors:: 错误对象
|
|
81
|
+
def json_failed_response(msg, errors = nil, template = nil)
|
|
82
|
+
@code = 500
|
|
83
|
+
@message = msg
|
|
84
|
+
@errors = errors
|
|
85
|
+
template ||= 'common/api'
|
|
86
|
+
render template, layout: 'json_with_meta', status: 400
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def response_after_save_json(result, obj)
|
|
90
|
+
if result
|
|
91
|
+
json_success_response
|
|
92
|
+
else
|
|
93
|
+
json_failed_response(view_context.tv('message_save_failed_with_msg', msg: obj.errors.full_messages), obj.errors)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ApplicationHelper
|
|
4
|
+
def body_id
|
|
5
|
+
ctrl_name = controller_path.gsub(%r{^v\d+/}, '').tr('/', '-')
|
|
6
|
+
[ctrl_name, action_name].map(&:dasherize).join('-')
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def body_class
|
|
10
|
+
controller_path.gsub(%r{^v\d+/}, '').tr('/', '-').dasherize
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def body_data_controller
|
|
14
|
+
@body_data_controller ||= controller_name.tr('_', '-').split('/').join('-')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def sort_opt(model, column)
|
|
18
|
+
{ model: model, column: column }.to_json
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DatatablesHelper
|
|
4
|
+
# Generate a html table tag for supporting ajax datatables
|
|
5
|
+
#
|
|
6
|
+
# == Parameters:
|
|
7
|
+
#
|
|
8
|
+
# options::
|
|
9
|
+
# Options which will be simply passed to content_tag method
|
|
10
|
+
#
|
|
11
|
+
# block::
|
|
12
|
+
# Content for thead, which is required for ajax datatable
|
|
13
|
+
#
|
|
14
|
+
# == Usage:
|
|
15
|
+
#
|
|
16
|
+
# <%= dt id: 'entities-list', class: 'display', :'data-url' => info_articles_path(:format => :json),
|
|
17
|
+
# :'data-sorting_column' => (if current_sys.selfdrive? then 4 else 5 end), :'data-sorting_dir' => 'desc' do %>
|
|
18
|
+
# <%= dt_col(data: { m_data_prop: 'id', s_type: 'numeric', s_width: '50px' }){ InfoArticle.human_attribute_name :id } %>
|
|
19
|
+
# ...
|
|
20
|
+
# <%= dt_col(data: { m_data_prop: 'actions', s_width: '70px', b_sortable: 'false' }) { t 'actions' } %>
|
|
21
|
+
# <% end %>
|
|
22
|
+
#
|
|
23
|
+
def datatable_tag(options)
|
|
24
|
+
# User autoWidth to calculate width, and use scrollX to allow scroll table, it will cause some black on the right of table, so need to set 100% width
|
|
25
|
+
# Pass in options params to add additional configs
|
|
26
|
+
content_tag :table, { width: '100%' }.merge(options) do
|
|
27
|
+
html = content_tag(:thead) { yield } || ''
|
|
28
|
+
html << content_tag(:tbody)
|
|
29
|
+
raw html
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
alias dt datatable_tag
|
|
33
|
+
|
|
34
|
+
# Similiar to @datatable_tag method but this will pass section as the block parameter,
|
|
35
|
+
# means in the block, we have to render sections accordingly.
|
|
36
|
+
# We support :thead, :tbody and :tfoot right now
|
|
37
|
+
def datatable_tag_section(options)
|
|
38
|
+
content_tag :table, options do
|
|
39
|
+
html = content_tag(:thead) { yield(:thead) } || ''
|
|
40
|
+
html << content_tag(:tbody) { yield(:tbody) }
|
|
41
|
+
html << content_tag(:tfoot) do
|
|
42
|
+
content_tag(:tr) { yield(:tfoot) }
|
|
43
|
+
end
|
|
44
|
+
raw html
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
alias dts datatable_tag_section
|
|
48
|
+
|
|
49
|
+
# Generate th html tag for building up ajax datatable html tag
|
|
50
|
+
#
|
|
51
|
+
# == Parameters:
|
|
52
|
+
#
|
|
53
|
+
# options::
|
|
54
|
+
# Options which will be passed to content_tag method. To be awared, an node named "data" will be treated as
|
|
55
|
+
# an hash, and it will be used to build HTML options with pattern like "data-*"
|
|
56
|
+
#
|
|
57
|
+
# == Usage:
|
|
58
|
+
# To render a datatable column with data property id:
|
|
59
|
+
# dt_col(data: { m_data_prop: 'id', s_type: 'numeric', s_width: '50px' }){ InfoArticle.human_attribute_name :id }
|
|
60
|
+
# This will render:
|
|
61
|
+
# <th data-m_data_prop="id" data-s_type="numeric" data-s_width="50px">编号</th>
|
|
62
|
+
#
|
|
63
|
+
# To render a datatable column with data property actions, with sorting feature disabled:
|
|
64
|
+
# dt_col(data: { m_data_prop: 'actions', s_width: '70px', b_sortable: 'false' }) { t 'actions' }
|
|
65
|
+
# This will render:
|
|
66
|
+
# <th data-b_sortable="0" data-m_data_prop="actions" data-s_width="70px">操作</th>
|
|
67
|
+
#
|
|
68
|
+
def datatable_col_tag(name = nil, options = {})
|
|
69
|
+
options = name if block_given?
|
|
70
|
+
data_options = options.delete :data
|
|
71
|
+
# Build up data options
|
|
72
|
+
data = {}
|
|
73
|
+
data_options.each { |k, v| data["data-#{k}"] = v } if data
|
|
74
|
+
if block_given?
|
|
75
|
+
content_tag(:th, options.merge(data)) { yield }
|
|
76
|
+
else
|
|
77
|
+
content_tag(:th, name, options.merge(data))
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
alias dt_col datatable_col_tag
|
|
81
|
+
|
|
82
|
+
# Shortcut method for datatable_col_tag for those columns with needs for only data attributes
|
|
83
|
+
#
|
|
84
|
+
# == Parameters:
|
|
85
|
+
# col_title::
|
|
86
|
+
# Column title
|
|
87
|
+
# data_options::
|
|
88
|
+
# Hash represent data attributes
|
|
89
|
+
#
|
|
90
|
+
# == Usage:
|
|
91
|
+
# <%= d_col InfoArticle.human_attribute_name(:info_column_id ), m_data_prop: 'info_column_name', s_width: '60px' %>
|
|
92
|
+
def datatable_col_tag_dataonly(col_title, data_options)
|
|
93
|
+
datatable_col_tag(data: data_options) { col_title }
|
|
94
|
+
end
|
|
95
|
+
alias d_col datatable_col_tag_dataonly
|
|
96
|
+
|
|
97
|
+
# A short function to wrap actions group for table rows with edit, delete buttons and others.
|
|
98
|
+
#
|
|
99
|
+
# == Parameters:
|
|
100
|
+
# edit_target::
|
|
101
|
+
# Link target for editing
|
|
102
|
+
# del_target::
|
|
103
|
+
# Link target for deleting
|
|
104
|
+
# other_target::
|
|
105
|
+
# Link targets for other actions
|
|
106
|
+
# opts::
|
|
107
|
+
# Options to be passed to the buttons, :edit for edit button and :del for delete button as well as others
|
|
108
|
+
#
|
|
109
|
+
# == Usage:
|
|
110
|
+
# edit_and_del(edit_route_path(row), route_path(row), [], {edit: {data:{id: 1}, class: ''}, del: {data:{id: 1}, class: ''}})
|
|
111
|
+
#
|
|
112
|
+
def render_edit_and_del_actions(edit_target, del_target, other_actions = [], opts = {})
|
|
113
|
+
actions = [{
|
|
114
|
+
target: edit_target,
|
|
115
|
+
link_text: '编辑',
|
|
116
|
+
options: opts.delete(:edit)
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
target: del_target,
|
|
120
|
+
link_text: '删除',
|
|
121
|
+
options: { method: :delete }.merge(opts.delete(:delete))
|
|
122
|
+
}]
|
|
123
|
+
|
|
124
|
+
if other_actions.any?
|
|
125
|
+
other_actions.each do |action|
|
|
126
|
+
key = action.keys.first
|
|
127
|
+
actions << {
|
|
128
|
+
target: action[key][:target],
|
|
129
|
+
link_text: action[key][:link_text],
|
|
130
|
+
options: { method: action[key][:method] }.merge(opts.delete(key))
|
|
131
|
+
}
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
row_actions actions
|
|
136
|
+
end
|
|
137
|
+
alias edit_and_del render_edit_and_del_actions
|
|
138
|
+
|
|
139
|
+
# Generate HTML codes for array of buttons for each row in a html grid.
|
|
140
|
+
#
|
|
141
|
+
# == Parameters:
|
|
142
|
+
# buttons::
|
|
143
|
+
# An array contains buttons information to be included in HTML table row, which normally will be the last col.
|
|
144
|
+
# Ruby link_to function will be used for each button in the array, each button of the array will be an hash,
|
|
145
|
+
# it should contained following information:
|
|
146
|
+
# :target: Link target
|
|
147
|
+
# :link_text: Text to include between begin and end tag of an A ancor
|
|
148
|
+
# :button_style: Button style class, if this has been indicated, this function won't apply it's default class
|
|
149
|
+
# :options: Extra options to be assigned to the link_to function, except the :class key, it will be combinted
|
|
150
|
+
# with some default css classes
|
|
151
|
+
# == Returns:
|
|
152
|
+
# HTML codes to be inserted into html table row cell, to present actions in each table row.
|
|
153
|
+
#
|
|
154
|
+
# == Usage:
|
|
155
|
+
# row_actions [
|
|
156
|
+
# { target: edit_route_path(row), link_text: icon(:edit), options: data {data: id} },
|
|
157
|
+
# {
|
|
158
|
+
# target: route_path(row, :format => :json), link_text: icon(:'trash-o'), button_style: :'btn-danger',
|
|
159
|
+
# options: { :remote => true, :method => :delete, :data => { :confirm => tv('delete_row_confirm') } }
|
|
160
|
+
# }
|
|
161
|
+
# ]
|
|
162
|
+
#
|
|
163
|
+
def render_table_row_actions(buttons)
|
|
164
|
+
actions = "<div class='row-actions'>".dup
|
|
165
|
+
buttons.each do |b|
|
|
166
|
+
b[:options] ||= {}
|
|
167
|
+
actions << link_to(raw(b[:link_text]), b[:target], b[:options])
|
|
168
|
+
end
|
|
169
|
+
actions << '</li></ul></div>'
|
|
170
|
+
end
|
|
171
|
+
alias row_actions render_table_row_actions
|
|
172
|
+
|
|
173
|
+
def render_multi_values(items)
|
|
174
|
+
actions = '<span>'.dup
|
|
175
|
+
items.each do |item|
|
|
176
|
+
actions << "#{item || '/'}<br>"
|
|
177
|
+
end
|
|
178
|
+
actions.chomp('<br>') << '</span>'
|
|
179
|
+
end
|
|
180
|
+
alias multi_values render_multi_values
|
|
181
|
+
|
|
182
|
+
# Helper to be used in jbuilder for rendering datatable json response
|
|
183
|
+
#
|
|
184
|
+
# == Usages:
|
|
185
|
+
# datatable_json_response do
|
|
186
|
+
# json.foo 'bar'
|
|
187
|
+
# end
|
|
188
|
+
#
|
|
189
|
+
# @return Datatable json response
|
|
190
|
+
def datatable_json_response(json, &block)
|
|
191
|
+
json.draw params['draw'] || 0
|
|
192
|
+
json.recordsTotal @total_rows if @total_rows
|
|
193
|
+
json.recordsFiltered @total_rows_filtered || @total_rows if @total_rows || @total_rows
|
|
194
|
+
if defined?(@error)
|
|
195
|
+
json.error @error
|
|
196
|
+
else
|
|
197
|
+
json.data(&block)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def sort_opt(model, column)
|
|
202
|
+
{ model: model, column: column }.to_json
|
|
203
|
+
end
|
|
204
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
section#wrapper
|
|
2
|
+
.login-register
|
|
3
|
+
.login-box.card
|
|
4
|
+
.card-body
|
|
5
|
+
= form_for resource,
|
|
6
|
+
as: resource_name,
|
|
7
|
+
url: session_path(resource_name),
|
|
8
|
+
html: { class: 'form-horizontal form-material' } do |f|
|
|
9
|
+
|
|
10
|
+
h3.box-title.m-b-20 Sign In
|
|
11
|
+
|
|
12
|
+
.form-group
|
|
13
|
+
.col-xs-12
|
|
14
|
+
= f.email_field :email, autofocus: true, autocomplete: "email", class: 'form-control'
|
|
15
|
+
|
|
16
|
+
.form-group
|
|
17
|
+
.col-xs-12
|
|
18
|
+
= f.password_field :password, autocomplete: "current-password", class: 'form-control'
|
|
19
|
+
|
|
20
|
+
- if devise_mapping.rememberable?
|
|
21
|
+
.form-group
|
|
22
|
+
.d-flex.no-block.align-items-center
|
|
23
|
+
.checkbox.checkbox-primary.p-t-0
|
|
24
|
+
= f.check_box :remember_me
|
|
25
|
+
= f.label :remember_me
|
|
26
|
+
|
|
27
|
+
.form-group.text-center.m-t-20
|
|
28
|
+
.col-xs-12
|
|
29
|
+
= f.submit "Log in", class: 'btn btn-info btn-lg btn-block text-uppercase waves-effect waves-light'
|
|
30
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
doctype html
|
|
2
|
+
html xmlns:wb="http://open.weibo.com/wb"
|
|
3
|
+
head
|
|
4
|
+
title
|
|
5
|
+
| Admin Panel
|
|
6
|
+
meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
|
7
|
+
= csp_meta_tag
|
|
8
|
+
= csrf_meta_tags
|
|
9
|
+
= javascript_pack_tag 'admin', 'data-turbolinks-track': 'reload'
|
|
10
|
+
= stylesheet_pack_tag 'admin_style', media: 'all', 'data-turbolinks-track': 'reload'
|
|
11
|
+
|
|
12
|
+
body[
|
|
13
|
+
id=body_id
|
|
14
|
+
class="fix-header fix-sidebar card-no-border #{body_class}"
|
|
15
|
+
data-controller=body_data_controller
|
|
16
|
+
]
|
|
17
|
+
/! Preloader - style you can find in spinners.css
|
|
18
|
+
// NOTE: Need fix for turbolinks.
|
|
19
|
+
/ .preloader
|
|
20
|
+
/ svg.circular viewbox=("25 25 50 50")
|
|
21
|
+
/ circle.path cx="50" cy="50" fill="none" r="20" stroke-miterlimit="10" stroke-width="2" /
|
|
22
|
+
|
|
23
|
+
/! Main wrapper - style you can find in pages.scss
|
|
24
|
+
#main-wrapper
|
|
25
|
+
= render 'admin/shared/header'
|
|
26
|
+
= render 'admin/shared/sidebar'
|
|
27
|
+
|
|
28
|
+
.page-wrapper
|
|
29
|
+
.container-fluid
|
|
30
|
+
= yield :breadcrumb
|
|
31
|
+
.row
|
|
32
|
+
= yield
|
|
33
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
doctype html
|
|
2
|
+
html
|
|
3
|
+
head
|
|
4
|
+
meta content=("text/html; charset=UTF-8") http-equiv="Content-Type" /
|
|
5
|
+
title
|
|
6
|
+
| Admin Panel
|
|
7
|
+
= csrf_meta_tags
|
|
8
|
+
= csp_meta_tag
|
|
9
|
+
= stylesheet_pack_tag 'admin_style', media: 'all', 'data-turbolinks-track': 'reload'
|
|
10
|
+
= javascript_pack_tag 'admin', 'data-turbolinks-track': 'reload'
|
|
11
|
+
|
|
12
|
+
body
|
|
13
|
+
= yield
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
- title = local_assigns[:title] ? title : []
|
|
2
|
+
- links = local_assigns[:links] ? links : []
|
|
3
|
+
- buttons = local_assigns[:buttons] ? buttons : []
|
|
4
|
+
- data_options = local_assigns[:data_options] ? options : {}
|
|
5
|
+
|
|
6
|
+
.row.page-titles
|
|
7
|
+
.col-md-6.col-8.align-self-center
|
|
8
|
+
h3.text-themecolor.m-b-0.m-t-0 = title
|
|
9
|
+
ol.breadcrumb
|
|
10
|
+
- links.each do |ele|
|
|
11
|
+
li.breadcrumb-item
|
|
12
|
+
- if ele[:link]
|
|
13
|
+
= link_to ele[:text], ele[:link]
|
|
14
|
+
- else
|
|
15
|
+
span = ele[:text]
|
|
16
|
+
|
|
17
|
+
.col-md-6.col-4.align-self-center.text-right
|
|
18
|
+
- buttons.each do |btn|
|
|
19
|
+
= link_to btn[:link], id: btn[:id], class: btn[:class], data: btn[:data_options] do
|
|
20
|
+
- if btn[:icon]
|
|
21
|
+
i class="fa fa-#{btn[:icon]}"
|
|
22
|
+
span.ml5 = btn[:text]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/! Topbar header - style you can find in pages.scss
|
|
2
|
+
header.topbar
|
|
3
|
+
nav.navbar.top-navbar.navbar-expand-md.navbar-light
|
|
4
|
+
/! Logo
|
|
5
|
+
.navbar-header
|
|
6
|
+
a.navbar-brand href="/" tppabs="https://wrappixel.com/demos/admin-templates/material-pro/material/index.html"
|
|
7
|
+
/! Logo icon
|
|
8
|
+
b
|
|
9
|
+
/! You can put here icon as well // <i class="wi wi-sunset"></i> //
|
|
10
|
+
/! Dark Logo icon
|
|
11
|
+
/ img.dark-logo alt="homepage" src="../assets/images/logo-icon.png" /
|
|
12
|
+
/! Light Logo icon
|
|
13
|
+
/ img.light-logo alt="homepage" src="../assets/images/logo-light-icon.png" /
|
|
14
|
+
/ = image_tag nil, width: 45, class: 'light-logo'
|
|
15
|
+
span.inline-block.ml10 Logo
|
|
16
|
+
.navbar-collapse
|
|
17
|
+
ul.navbar-nav.mr-auto.mt-md-0
|
|
18
|
+
li.nav-item
|
|
19
|
+
a.nav-link.nav-toggler.hidden-md-up.text-muted.waves-effect.waves-dark href="javascript:void(0)"
|
|
20
|
+
i.mdi.mdi-menu
|
|
21
|
+
li.nav-item
|
|
22
|
+
a.nav-link.sidebartoggler.hidden-sm-down.text-muted.waves-effect.waves-dark href="javascript:void(0)"
|
|
23
|
+
i.ti-menu
|
|
24
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
- if controller_name != 'sessions'
|
|
2
|
+
= link_to "Log in", new_session_path(resource_name)
|
|
3
|
+
br
|
|
4
|
+
- if devise_mapping.registerable? && controller_name != 'registrations'
|
|
5
|
+
= link_to "Sign up", new_registration_path(resource_name)
|
|
6
|
+
br
|
|
7
|
+
- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations'
|
|
8
|
+
= link_to "Forgot your password?", new_password_path(resource_name)
|
|
9
|
+
br
|
|
10
|
+
- if devise_mapping.confirmable? && controller_name != 'confirmations'
|
|
11
|
+
= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name)
|
|
12
|
+
br
|
|
13
|
+
- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks'
|
|
14
|
+
= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name)
|
|
15
|
+
br
|
|
16
|
+
- if devise_mapping.omniauthable?
|
|
17
|
+
- resource_class.omniauth_providers.each do |provider|
|
|
18
|
+
= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider)
|
|
19
|
+
br
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/* Required parameters:
|
|
2
|
+
* name: ''
|
|
3
|
+
* radios: [{ id: '', value: '', text: '' }]
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
- name = local_assigns[:name] ? name : ''
|
|
7
|
+
- radios = local_assigns[:radios] ? radios : []
|
|
8
|
+
|
|
9
|
+
- radios.each_with_index do |radio, i|
|
|
10
|
+
= radio_button_tag name,
|
|
11
|
+
radio[:value],
|
|
12
|
+
radio[:checked],
|
|
13
|
+
class: 'radio-col-cyan',
|
|
14
|
+
id: radio[:id],
|
|
15
|
+
disabled: radio[:disabled],
|
|
16
|
+
data: radio[:opts]
|
|
17
|
+
label for=radio[:id] = radio[:text]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/! ==============================================================
|
|
2
|
+
/! Left Sidebar - style you can find in sidebar.scss
|
|
3
|
+
/! ==============================================================
|
|
4
|
+
aside.left-sidebar
|
|
5
|
+
/! Sidebar scroll
|
|
6
|
+
.scroll-sidebar
|
|
7
|
+
/! Sidebar navigation
|
|
8
|
+
nav.sidebar-nav
|
|
9
|
+
ul#sidebarnav
|
|
10
|
+
/ li.nav-small-cap Project title
|
|
11
|
+
li
|
|
12
|
+
= link_to '#' do
|
|
13
|
+
i.fa.fa-user
|
|
14
|
+
span.hide-menu Menu
|
|
15
|
+
|
|
16
|
+
/! Bottom points
|
|
17
|
+
.sidebar-footer
|
|
18
|
+
/ a.link data-toggle="tooltip" href="" title="Settings"
|
|
19
|
+
/ i.ti-settings
|
|
20
|
+
/ a.link data-toggle="tooltip" href="" title="Email"
|
|
21
|
+
/ i.mdi.mdi-gmail
|
|
22
|
+
a.link data-toggle="tooltip" href=destroy_admin_session_path title="Logout" data-method="DELETE"
|
|
23
|
+
i.mdi.mdi-power
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const { environment } = require('@rails/webpacker')
|
|
2
|
+
|
|
3
|
+
const webpack = require('webpack');
|
|
4
|
+
|
|
5
|
+
environment.loaders.get('sass').use.splice(-1, 0, {
|
|
6
|
+
loader: 'resolve-url-loader'
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
environment.loaders.append('expose-loader', {
|
|
10
|
+
test: require.resolve('jquery'),
|
|
11
|
+
use: [{
|
|
12
|
+
loader: 'expose-loader',
|
|
13
|
+
options: '$'
|
|
14
|
+
}]
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
environment.plugins.append('Provide', new webpack.ProvidePlugin({
|
|
18
|
+
$: 'jquery',
|
|
19
|
+
jQuery: 'jquery',
|
|
20
|
+
"window.jQuery": 'jquery',
|
|
21
|
+
Popper: ['popper.js', 'default']
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
module.exports = environment
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CrudGenerator < Rails::Generators::NamedBase
|
|
4
|
+
include Rails::Generators::Actions
|
|
5
|
+
|
|
6
|
+
class_option :options, type: :string, default: ''
|
|
7
|
+
|
|
8
|
+
# NOTE: Must use singular for resource param
|
|
9
|
+
|
|
10
|
+
# TODO: customize dir prefix
|
|
11
|
+
WEBPACKER_DIR_PREFIX = 'javascript'
|
|
12
|
+
|
|
13
|
+
def create_files
|
|
14
|
+
p 'Create js controller file...'
|
|
15
|
+
|
|
16
|
+
create_file "#{Rails.root}/app/#{WEBPACKER_DIR_PREFIX}/src/javascripts/#{layout_name}/controllers/#{resource}_controller.js" do
|
|
17
|
+
<<~js
|
|
18
|
+
import { Controller } from "stimulus";
|
|
19
|
+
import initDatatable from '../../lib/utils_datatables.js';
|
|
20
|
+
|
|
21
|
+
export default class extends Controller {
|
|
22
|
+
connect() {
|
|
23
|
+
if (!$('body').hasClass('#{layout_name}-#{resource}')) return false;
|
|
24
|
+
|
|
25
|
+
if (!$('table##{resource}-datatable').hasClass('dataTable')) {
|
|
26
|
+
initDatatable($('##{resource}-datatable'), { searching: true });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
js
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
p 'Create resource template files...'
|
|
34
|
+
|
|
35
|
+
create_file "#{Rails.root}/app/views/#{layout_name}/#{resource}/index.html.slim" do
|
|
36
|
+
<<~html
|
|
37
|
+
= content_for :breadcrumb do
|
|
38
|
+
= render '#{layout_name}/shared/breadcrumb',
|
|
39
|
+
title: ''
|
|
40
|
+
|
|
41
|
+
.col-12
|
|
42
|
+
.card
|
|
43
|
+
.card-body
|
|
44
|
+
= dt id: '#{resource}-datatable', class: 'table', 'data-url': #{layout_name}_#{resource}_path do
|
|
45
|
+
= dt_col('email', data: { data: 'email' })
|
|
46
|
+
| TODO
|
|
47
|
+
html
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
create_file "#{Rails.root}/app/views/#{layout_name}/#{resource}/new.html.slim" do
|
|
51
|
+
<<~html
|
|
52
|
+
= content_for :breadcrumb do
|
|
53
|
+
= render 'admin/shared/breadcrumb',
|
|
54
|
+
title: '#{resource.camelize}',
|
|
55
|
+
links: [{ text: '#{resource.camelize}', link: admin_#{resource}_path }, { text: '添加' }]
|
|
56
|
+
|
|
57
|
+
= render 'form', url: admin_#{resource}_path
|
|
58
|
+
html
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
create_file "#{Rails.root}/app/views/#{layout_name}/#{resource}/edit.html.slim" do
|
|
62
|
+
<<~html
|
|
63
|
+
= content_for :breadcrumb do
|
|
64
|
+
= render 'admin/shared/breadcrumb',
|
|
65
|
+
title: '#{resource.camelize}',
|
|
66
|
+
links: [{ text: '#{resource.camelize}', link: admin_#{resource}_path }, { text: '编辑' }]
|
|
67
|
+
|
|
68
|
+
= render 'form', url: admin_#{name}_path
|
|
69
|
+
html
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
create_file "#{Rails.root}/app/views/#{layout_name}/#{resource}/_form.html.slim" do
|
|
73
|
+
<<~html
|
|
74
|
+
.col-lg-12
|
|
75
|
+
.card
|
|
76
|
+
.card-body
|
|
77
|
+
= form_for @#{name}, url: url, html: { class: 'form-horizontal' } do |f|
|
|
78
|
+
.form-body
|
|
79
|
+
.form-group.row
|
|
80
|
+
// TODO:
|
|
81
|
+
// = f.label :'', '', class: 'control-label text-right col-md-3'
|
|
82
|
+
// .col-md-6
|
|
83
|
+
// = f.text_field :'', class: 'form-control', required: true, placeholder: ''
|
|
84
|
+
|
|
85
|
+
.form-actions.text-center
|
|
86
|
+
= f.submit '保存', class: 'btn btn-success waves-effect waves-light m-r-10'
|
|
87
|
+
= link_to '返回', admin_#{resource}_path, class: 'btn btn-inverse waves-effect waves-light'
|
|
88
|
+
html
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
p 'Create resource index json file...'
|
|
92
|
+
|
|
93
|
+
create_file "#{Rails.root}/app/views/#{layout_name}/#{resource}/index.json.jbuilder" do
|
|
94
|
+
<<~html
|
|
95
|
+
# frozen_string_literal: true
|
|
96
|
+
|
|
97
|
+
datatable_json_response(json) do
|
|
98
|
+
json.array! @rows do |row|
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
html
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
p 'Create resource controller file...'
|
|
105
|
+
|
|
106
|
+
create_file "#{Rails.root}/app/controllers/#{layout_name}/#{resource}_controller.rb" do
|
|
107
|
+
<<~controller
|
|
108
|
+
# frozen_string_literal: true
|
|
109
|
+
|
|
110
|
+
class #{layout_name.camelize}::#{resource.camelize}Controller < #{layout_name.camelize}::BaseController
|
|
111
|
+
|
|
112
|
+
include DatatableDecorator
|
|
113
|
+
|
|
114
|
+
def index
|
|
115
|
+
respond_to do |format|
|
|
116
|
+
format.html
|
|
117
|
+
format.json { fetch_#{resource} }
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def new
|
|
122
|
+
@#{name} = #{name.camelize}.new
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def edit
|
|
126
|
+
@#{name} = #{name.camelize}.find(params[:id])
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
def fetch_#{resource}
|
|
132
|
+
dt = decode_datatable_params
|
|
133
|
+
|
|
134
|
+
search_obj = { order: dt[:sort_statement] }
|
|
135
|
+
|
|
136
|
+
search_text = dt[:search_text]
|
|
137
|
+
|
|
138
|
+
#{resource} =
|
|
139
|
+
if search_text.present?
|
|
140
|
+
#{name.camelize}.where('nickname ilike ?', "%\#\{search_text\}\%")
|
|
141
|
+
else
|
|
142
|
+
#{name.camelize}.all
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
@total_rows = #{resource}.count
|
|
146
|
+
|
|
147
|
+
@rows = users.page(dt[:page]).per(dt[:per_page])
|
|
148
|
+
@rows = @rows.order(search_obj[:order])
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
controller
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
p "Create #{name} model..."
|
|
155
|
+
|
|
156
|
+
system("rails generate model #{name}")
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def need_modify
|
|
160
|
+
p '****************************************************'
|
|
161
|
+
p '****************************************************'
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def prompt_modify_migration
|
|
165
|
+
p "Need change #{resource} migration file then run migration manually..."
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def prompt_add_routes
|
|
169
|
+
p "Need add routes for #{resource} in #{layout_name} namespace manually..."
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def prompt_uncomment_stimulus_controllers
|
|
173
|
+
p 'Remember to uncomment stimulus js controllers'
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def end
|
|
177
|
+
p '****************************************************'
|
|
178
|
+
p '****************************************************'
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
private
|
|
182
|
+
|
|
183
|
+
def resource
|
|
184
|
+
name.pluralize
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def layout_name
|
|
188
|
+
opts.fetch(:layout_name, 'admin')
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def opts
|
|
192
|
+
options['options'].split(' ').map { |ele| ele.split(':') }.to_h.symbolize_keys
|
|
193
|
+
end
|
|
194
|
+
end
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class MaterialAdminGenerator < Rails::Generators::NamedBase
|
|
4
|
+
include Rails::Generators::Actions
|
|
5
|
+
|
|
6
|
+
GEM_NAME = 'material_admin'
|
|
7
|
+
GEM_PATH = Gem.loaded_specs[GEM_NAME].full_gem_path
|
|
8
|
+
JS_PKGS =
|
|
9
|
+
'datatables.net-bs4 datatables.net-responsive-bs4'\
|
|
10
|
+
' expose-loader file-loader url-loader resolve-url-loader'\
|
|
11
|
+
' rails-ujs stimulus turbolinks'\
|
|
12
|
+
' jquery popper.js select2'
|
|
13
|
+
|
|
14
|
+
# TODO: Warning! Add reset project.
|
|
15
|
+
|
|
16
|
+
# TODO: customize dir prefix
|
|
17
|
+
WEBPACKER_DIR_PREFIX = 'javascript'
|
|
18
|
+
|
|
19
|
+
class_option :layout_name, type: :string, default: 'admin'
|
|
20
|
+
class_option :options, type: :string, default: []
|
|
21
|
+
|
|
22
|
+
def add_gems
|
|
23
|
+
p 'Add gems...'
|
|
24
|
+
|
|
25
|
+
gem 'slim'
|
|
26
|
+
gem 'devise'
|
|
27
|
+
gem 'kaminari'
|
|
28
|
+
|
|
29
|
+
system('bundle install')
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def import_files
|
|
33
|
+
p 'Import material admin theme files...'
|
|
34
|
+
|
|
35
|
+
webpacker_dir = "#{Rails.root}/app/#{WEBPACKER_DIR_PREFIX}"
|
|
36
|
+
|
|
37
|
+
FileUtils.mkdir_p("#{webpacker_dir}/vendor") unless File.directory?("#{webpacker_dir}/vendor")
|
|
38
|
+
|
|
39
|
+
FileUtils.copy_entry("#{GEM_PATH}/vendor/#{GEM_NAME}", "#{webpacker_dir}/vendor/#{GEM_NAME}/")
|
|
40
|
+
|
|
41
|
+
p 'Import package.json...'
|
|
42
|
+
|
|
43
|
+
FileUtils.cp "#{GEM_PATH}/package.json", "#{Rails.root}/package.json"
|
|
44
|
+
|
|
45
|
+
p 'Import js source files...'
|
|
46
|
+
|
|
47
|
+
FileUtils.copy_entry("#{GEM_PATH}/vendor/javascript", webpacker_dir)
|
|
48
|
+
|
|
49
|
+
p 'Import datatable files...'
|
|
50
|
+
|
|
51
|
+
FileUtils.cp(
|
|
52
|
+
"#{GEM_PATH}/app/controllers/concerns/datatable_decorator.rb",
|
|
53
|
+
"#{Rails.root}/app/controllers/concerns/datatable_decorator.rb"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
FileUtils.cp(
|
|
57
|
+
"#{GEM_PATH}/app/helpers/datatables_helper.rb",
|
|
58
|
+
"#{Rails.root}/app/helpers/datatables_helper.rb"
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def create_helper
|
|
63
|
+
p "Create #{layout_name} layout helper..."
|
|
64
|
+
|
|
65
|
+
create_file "#{Rails.root}/app/helpers/#{layout_name}_helper.rb" do
|
|
66
|
+
<<~helper
|
|
67
|
+
# frozen_string_literal: true
|
|
68
|
+
|
|
69
|
+
module #{layout_name.camelize}Helper
|
|
70
|
+
def body_id
|
|
71
|
+
ctrl_name = controller_path.gsub(%r{^v\d+/}, '').tr('/', '-')
|
|
72
|
+
[ctrl_name, action_name].map(&:dasherize).join('-')
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def body_class
|
|
76
|
+
controller_path.gsub(%r{^v\d+/}, '').tr('/', '-').dasherize
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def body_data_controller
|
|
80
|
+
@body_data_controller ||= controller_name.tr('_', '-').split('/').join('-')
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
helper
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def create_js_manifest
|
|
88
|
+
p "Create #{layout_name} js manifest..."
|
|
89
|
+
|
|
90
|
+
src = "#{GEM_PATH}/vendor/javascript/packs/admin.js"
|
|
91
|
+
dest = "#{Rails.root}/app/#{WEBPACKER_DIR_PREFIX}/packs/#{layout_name}.js"
|
|
92
|
+
|
|
93
|
+
FileUtils.cp src, dest
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def create_style_manifest
|
|
97
|
+
p "Create #{layout_name} style js manifest..."
|
|
98
|
+
|
|
99
|
+
src = "#{GEM_PATH}/vendor/javascript/packs/admin_style.js"
|
|
100
|
+
dest = "#{Rails.root}/app/#{WEBPACKER_DIR_PREFIX}/packs/#{layout_name}_style.js"
|
|
101
|
+
|
|
102
|
+
FileUtils.cp src, dest
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def create_layout
|
|
106
|
+
p "Create #{layout_name} layout..."
|
|
107
|
+
|
|
108
|
+
layout_html_path = "#{Rails.root}/app/views/layouts/#{layout_name}.html.erb"
|
|
109
|
+
File.rename(layout_html_path, "#{options['layout_name']}.html.slim") if File.exist?(layout_html_path)
|
|
110
|
+
|
|
111
|
+
src = "#{GEM_PATH}/app/views/layouts/admin.html.slim"
|
|
112
|
+
dest = "#{Rails.root}/app/views/layouts/#{layout_name}.html.slim"
|
|
113
|
+
|
|
114
|
+
FileUtils.cp src, dest
|
|
115
|
+
|
|
116
|
+
src = "#{GEM_PATH}/app/views/layouts/unauthorized.html.slim"
|
|
117
|
+
dest = "#{Rails.root}/app/views/layouts/unauthorized.html.slim"
|
|
118
|
+
|
|
119
|
+
FileUtils.cp src, dest
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def create_partials
|
|
123
|
+
p "Create #{layout_name} shared partials..."
|
|
124
|
+
|
|
125
|
+
dest = "#{Rails.root}/app/views/#{layout_name}/shared"
|
|
126
|
+
|
|
127
|
+
FileUtils.mkdir_p(dest) unless File.directory?(dest)
|
|
128
|
+
|
|
129
|
+
FileUtils.copy_entry("#{GEM_PATH}/app/views/shared", dest)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def yarn_install
|
|
133
|
+
system("yarn add #{JS_PKGS}")
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def create_base_controller
|
|
137
|
+
p "Create base controller'"
|
|
138
|
+
|
|
139
|
+
create_file "#{Rails.root}/app/controllers/#{layout_name}/base_controller.rb" do
|
|
140
|
+
<<~dashboard_controller
|
|
141
|
+
# frozen_string_literal: true
|
|
142
|
+
|
|
143
|
+
class #{layout_name.camelize}::BaseController < ActionController::Base
|
|
144
|
+
layout '#{layout_name}'
|
|
145
|
+
|
|
146
|
+
before_action :authenticate_#{layout_name}!
|
|
147
|
+
end
|
|
148
|
+
dashboard_controller
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def create_dashboard_controller
|
|
153
|
+
p 'Create dashboard controller...'
|
|
154
|
+
|
|
155
|
+
create_file "#{Rails.root}/app/controllers/#{layout_name}/dashboard_controller.rb" do
|
|
156
|
+
<<~dashboard_controller
|
|
157
|
+
# frozen_string_literal: true
|
|
158
|
+
|
|
159
|
+
class #{layout_name.camelize}::DashboardController < #{layout_name.camelize}::BaseController
|
|
160
|
+
def index
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
dashboard_controller
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def create_sessions_controller
|
|
168
|
+
p "Create #{layout_name} sessions controller..."
|
|
169
|
+
|
|
170
|
+
create_file "#{Rails.root}/app/controllers/#{layout_name}/sessions_controller.rb" do
|
|
171
|
+
<<~sessions_controller
|
|
172
|
+
# frozen_string_literal: true
|
|
173
|
+
|
|
174
|
+
class Admin::SessionsController < Devise::SessionsController
|
|
175
|
+
layout 'unauthorized'
|
|
176
|
+
end
|
|
177
|
+
sessions_controller
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def create_dashboard_view
|
|
182
|
+
p 'Create dashboard view...'
|
|
183
|
+
|
|
184
|
+
create_file "#{Rails.root}/app/views/#{layout_name}/dashboard/index.html.slim" do
|
|
185
|
+
<<~dashboard_page
|
|
186
|
+
h1 Welcome come back🙂
|
|
187
|
+
dashboard_page
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def create_new_session_file
|
|
192
|
+
p 'Create sign_in template...'
|
|
193
|
+
|
|
194
|
+
dir = "#{Rails.root}/app/views/#{layout_name}/sessions/"
|
|
195
|
+
|
|
196
|
+
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
|
197
|
+
|
|
198
|
+
FileUtils.cp(
|
|
199
|
+
"#{GEM_PATH}/app/views/#{layout_name}/sessions/new.html.slim",
|
|
200
|
+
"#{Rails.root}/app/views/#{layout_name}/sessions/new.html.slim"
|
|
201
|
+
)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def init_db
|
|
205
|
+
p 'Init db...'
|
|
206
|
+
|
|
207
|
+
system('rake db:drop db:create')
|
|
208
|
+
|
|
209
|
+
p 'Init devise...'
|
|
210
|
+
system('rails g devise:install')
|
|
211
|
+
system("rails generate model #{layout_name}")
|
|
212
|
+
system("rails generate devise #{layout_name}")
|
|
213
|
+
system('rake db:migrate')
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def modify_webpacker_environment_file
|
|
217
|
+
p 'Overwrite config webpack environment js file'
|
|
218
|
+
|
|
219
|
+
src = "#{GEM_PATH}/config/webpack/environment.js"
|
|
220
|
+
dest = "#{Rails.root}/config/webpack/environment.js"
|
|
221
|
+
|
|
222
|
+
FileUtils.cp src, dest
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def need_modify
|
|
226
|
+
p '****************************************************'
|
|
227
|
+
p '****************************************************'
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def prompt_confit_route
|
|
231
|
+
puts "Need change the devise default route to:\n"
|
|
232
|
+
routes = <<~routes
|
|
233
|
+
devise_for :#{layout_name},
|
|
234
|
+
path: '#{layout_name}',
|
|
235
|
+
controllers: { sessions: '#{layout_name}/sessions' }
|
|
236
|
+
|
|
237
|
+
namespace :#{layout_name} do
|
|
238
|
+
root 'dashboard#index'
|
|
239
|
+
end
|
|
240
|
+
routes
|
|
241
|
+
puts routes
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def prompt_extract_css
|
|
245
|
+
puts "Need set extract_css to true in webpacker.yml\n"
|
|
246
|
+
p 'extract_css: true'
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def at_end
|
|
250
|
+
p '****************************************************'
|
|
251
|
+
p '****************************************************'
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
private
|
|
255
|
+
|
|
256
|
+
def layout_name
|
|
257
|
+
options['layout_name']
|
|
258
|
+
end
|
|
259
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rails_material_admin
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Tracy Cai
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2020-06-08 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rails
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 5.2.4
|
|
20
|
+
- - ">="
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: 5.2.4.3
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - "~>"
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: 5.2.4
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 5.2.4.3
|
|
33
|
+
description: Quickly start admin page using Material Admin Pro theme.
|
|
34
|
+
email:
|
|
35
|
+
- 835010809@qq.com
|
|
36
|
+
executables: []
|
|
37
|
+
extensions: []
|
|
38
|
+
extra_rdoc_files: []
|
|
39
|
+
files:
|
|
40
|
+
- MIT-LICENSE
|
|
41
|
+
- README.md
|
|
42
|
+
- Rakefile
|
|
43
|
+
- app/controllers/concerns/datatable_decorator.rb
|
|
44
|
+
- app/helpers/admin_helper.rb
|
|
45
|
+
- app/helpers/datatables_helper.rb
|
|
46
|
+
- app/views/admin/sessions/new.html.slim
|
|
47
|
+
- app/views/layouts/admin.html.slim
|
|
48
|
+
- app/views/layouts/unauthorized.html.slim
|
|
49
|
+
- app/views/shared/_breadcrumb.html.slim
|
|
50
|
+
- app/views/shared/_error_messages.html.slim
|
|
51
|
+
- app/views/shared/_header.html.slim
|
|
52
|
+
- app/views/shared/_links.html.slim
|
|
53
|
+
- app/views/shared/_radio_box.html.slim
|
|
54
|
+
- app/views/shared/_sidebar.html.slim
|
|
55
|
+
- config/webpack/environment.js
|
|
56
|
+
- lib/generators/crud/crud_generator.rb
|
|
57
|
+
- lib/generators/material_admin/material_admin_generator.rb
|
|
58
|
+
- lib/material_admin.rb
|
|
59
|
+
- lib/material_admin/railtie.rb
|
|
60
|
+
- lib/material_admin/version.rb
|
|
61
|
+
- lib/tasks/material_admin_tasks.rake
|
|
62
|
+
homepage: https://github.com/NaiveCAI/material_admin_rails
|
|
63
|
+
licenses:
|
|
64
|
+
- MIT
|
|
65
|
+
metadata: {}
|
|
66
|
+
post_install_message:
|
|
67
|
+
rdoc_options: []
|
|
68
|
+
require_paths:
|
|
69
|
+
- lib
|
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0'
|
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
76
|
+
requirements:
|
|
77
|
+
- - ">="
|
|
78
|
+
- !ruby/object:Gem::Version
|
|
79
|
+
version: '0'
|
|
80
|
+
requirements: []
|
|
81
|
+
rubyforge_project:
|
|
82
|
+
rubygems_version: 2.7.10
|
|
83
|
+
signing_key:
|
|
84
|
+
specification_version: 4
|
|
85
|
+
summary: Quickly start admin page using Material Admin Pro theme.
|
|
86
|
+
test_files: []
|