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.
@@ -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: []