rails_material_admin 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|