rails_material_admin 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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.
@@ -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).
@@ -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,8 @@
1
+ - if resource.errors.any?
2
+ #error_explanation
3
+ h2
4
+ = I18n.t("errors.messages.not_saved", count: resource.errors.count, resource: resource.class.model_name.human.downcase)
5
+ ul
6
+ - resource.errors.full_messages.each do |message|
7
+ li
8
+ = message
@@ -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
@@ -0,0 +1,5 @@
1
+ require "material_admin/railtie"
2
+
3
+ module MaterialAdmin
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,4 @@
1
+ module MaterialAdmin
2
+ class Railtie < ::Rails::Railtie
3
+ end
4
+ end
@@ -0,0 +1,3 @@
1
+ module MaterialAdmin
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :material_admin do
3
+ # # Task goes here
4
+ # 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: []