ditty 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.pryrc +2 -0
  4. data/.rubocop.yml +1 -1
  5. data/.travis.yml +5 -4
  6. data/CNAME +1 -0
  7. data/Dockerfile +18 -0
  8. data/Gemfile.ci +0 -2
  9. data/Rakefile +2 -2
  10. data/_config.yml +1 -0
  11. data/config.ru +4 -4
  12. data/ditty.gemspec +9 -3
  13. data/docs/CNAME +1 -0
  14. data/docs/_config.yml +1 -0
  15. data/docs/index.md +34 -0
  16. data/exe/ditty +2 -0
  17. data/lib/ditty.rb +4 -2
  18. data/lib/ditty/cli.rb +28 -4
  19. data/lib/ditty/components/{app.rb → ditty.rb} +19 -14
  20. data/lib/ditty/controllers/{application.rb → application_controller.rb} +58 -29
  21. data/lib/ditty/controllers/{audit_logs.rb → audit_logs_controller.rb} +2 -2
  22. data/lib/ditty/controllers/{auth.rb → auth_controller.rb} +17 -16
  23. data/lib/ditty/controllers/{component.rb → component_controller.rb} +19 -18
  24. data/lib/ditty/controllers/{main.rb → main_controller.rb} +6 -2
  25. data/lib/ditty/controllers/roles_controller.rb +23 -0
  26. data/lib/ditty/controllers/{user_login_traits.rb → user_login_traits_controller.rb} +4 -3
  27. data/lib/ditty/controllers/{users.rb → users_controller.rb} +11 -10
  28. data/lib/ditty/db.rb +4 -3
  29. data/lib/ditty/emails/base.rb +5 -2
  30. data/lib/ditty/generators/crud_generator.rb +104 -0
  31. data/lib/ditty/generators/migration_generator.rb +26 -0
  32. data/lib/ditty/generators/project_generator.rb +51 -0
  33. data/lib/ditty/helpers/component.rb +2 -1
  34. data/lib/ditty/helpers/pundit.rb +20 -4
  35. data/lib/ditty/helpers/response.rb +20 -13
  36. data/lib/ditty/helpers/views.rb +7 -3
  37. data/lib/ditty/listener.rb +5 -3
  38. data/lib/ditty/memcached.rb +8 -0
  39. data/lib/ditty/middleware/accept_extension.rb +2 -2
  40. data/lib/ditty/middleware/error_catchall.rb +2 -2
  41. data/lib/ditty/models/base.rb +4 -0
  42. data/lib/ditty/models/role.rb +1 -0
  43. data/lib/ditty/models/user.rb +14 -1
  44. data/lib/ditty/policies/role_policy.rb +1 -1
  45. data/lib/ditty/policies/user_login_trait_policy.rb +1 -1
  46. data/lib/ditty/services/authentication.rb +11 -10
  47. data/lib/ditty/services/email.rb +8 -4
  48. data/lib/ditty/services/logger.rb +1 -1
  49. data/lib/ditty/tasks/ditty.rake +17 -0
  50. data/lib/ditty/tasks/omniauth-ldap.rake +2 -2
  51. data/lib/ditty/templates/.gitignore +5 -0
  52. data/lib/ditty/templates/.rspec +2 -0
  53. data/lib/ditty/templates/.rubocop.yml +7 -0
  54. data/lib/ditty/templates/Rakefile +12 -0
  55. data/lib/ditty/templates/application.rb +12 -0
  56. data/lib/ditty/templates/config.ru +37 -0
  57. data/lib/ditty/templates/controller.rb.erb +58 -0
  58. data/lib/ditty/templates/env.example +4 -0
  59. data/lib/ditty/templates/lib/project.rb.erb +5 -0
  60. data/lib/ditty/templates/migration.rb.erb +7 -0
  61. data/lib/ditty/templates/model.rb.erb +26 -0
  62. data/lib/ditty/templates/pids/.empty_directory +0 -0
  63. data/lib/ditty/templates/policy.rb.erb +48 -0
  64. data/lib/ditty/templates/public/css/sb-admin-2.min.css +10 -0
  65. data/lib/ditty/templates/public/js/sb-admin-2.min.js +7 -0
  66. data/lib/ditty/templates/settings.yml.erb +18 -0
  67. data/lib/ditty/templates/sidekiq.rb +18 -0
  68. data/lib/ditty/templates/sidekiq.yml +9 -0
  69. data/lib/ditty/templates/spec_helper.rb +43 -0
  70. data/lib/ditty/templates/type.rb.erb +21 -0
  71. data/lib/ditty/templates/views/display.haml.tt +20 -0
  72. data/lib/ditty/templates/views/edit.haml.tt +10 -0
  73. data/lib/ditty/templates/views/form.haml.tt +11 -0
  74. data/lib/ditty/templates/views/index.haml.tt +29 -0
  75. data/lib/ditty/templates/views/new.haml.tt +10 -0
  76. data/lib/ditty/version.rb +1 -1
  77. data/lib/rubocop/cop/ditty/call_services_directly.rb +2 -2
  78. data/migrate/20181209_add_user_login_traits.rb +4 -4
  79. data/migrate/20190220_add_parent_id_to_roles.rb +9 -0
  80. data/public/css/styles.css +13 -0
  81. data/public/js/scripts.js +1 -0
  82. data/views/404.haml +2 -4
  83. data/views/audit_logs/index.haml +32 -34
  84. data/views/auth/forgot_password.haml +27 -16
  85. data/views/auth/identity.haml +14 -13
  86. data/views/auth/ldap.haml +2 -2
  87. data/views/auth/login.haml +22 -17
  88. data/views/auth/register.haml +19 -18
  89. data/views/auth/register_identity.haml +27 -12
  90. data/views/auth/reset_password.haml +2 -2
  91. data/views/blank.haml +42 -0
  92. data/views/index.haml +1 -1
  93. data/views/layout.haml +37 -30
  94. data/views/partials/content_tag.haml +0 -0
  95. data/views/partials/delete_form.haml +1 -1
  96. data/views/partials/filter_control.haml +1 -1
  97. data/views/partials/footer.haml +5 -5
  98. data/views/partials/form_control.haml +19 -12
  99. data/views/partials/navitems.haml +44 -0
  100. data/views/partials/notifications.haml +12 -8
  101. data/views/partials/pager.haml +17 -17
  102. data/views/partials/search.haml +6 -7
  103. data/views/partials/sidebar.haml +15 -37
  104. data/views/partials/topbar.haml +68 -0
  105. data/views/roles/display.haml +27 -6
  106. data/views/roles/edit.haml +3 -3
  107. data/views/roles/form.haml +1 -0
  108. data/views/roles/index.haml +23 -16
  109. data/views/roles/new.haml +2 -2
  110. data/views/user_login_traits/display.haml +4 -4
  111. data/views/user_login_traits/edit.haml +3 -3
  112. data/views/user_login_traits/index.haml +4 -4
  113. data/views/user_login_traits/new.haml +2 -2
  114. data/views/users/display.haml +11 -12
  115. data/views/users/edit.haml +3 -3
  116. data/views/users/form.haml +0 -0
  117. data/views/users/index.haml +31 -24
  118. data/views/users/login_traits.haml +6 -8
  119. data/views/users/new.haml +2 -2
  120. data/views/users/profile.haml +13 -13
  121. metadata +143 -15
  122. data/lib/ditty/controllers/roles.rb +0 -13
  123. data/views/partials/navbar.haml +0 -22
@@ -1,8 +1,8 @@
1
1
  .row
2
2
  .col-md-2
3
3
  .col-md-8
4
- .panel.panel-default
5
- .panel-body
4
+ .card.card-default
5
+ .card-body
6
6
  = edit_form_tag("#{settings.map_path}/auth/reset-password") do
7
7
  %input{ name: 'token', value: params[:token], type: 'hidden' }
8
8
  - if identity.errors[:password] && identity.errors[:password].include?('is not strong enough')
@@ -0,0 +1,42 @@
1
+ !!! 5
2
+ %html{ lang: 'en' }
3
+ %head
4
+ %meta{ charset: 'utf-8' }
5
+ %meta{ 'http-equiv' => 'X-UA-Compatible', 'content' => 'IE=edge,chrome=1' }
6
+ %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
7
+ %meta{ name: 'theme-color', content: '#ffffff' }
8
+ %link{ rel: 'manifest', href: '/manifest.json' }
9
+ %link{ rel: 'icon', type: 'image/png', sizes: '32x32', href: '/images/favicon-32x32.png' }
10
+ %link{ rel: 'icon', type: 'image/png', sizes: '16x16', href: '/images/favicon-16x16.png' }
11
+ %link{ rel: 'apple-touch-icon', sizes: '76x76', href: '/images/apple-icon.png' }
12
+ %link{ rel: 'mask-icon', href: '/safari-pinned-tab.svg', color: '#5bbad5' }
13
+
14
+ %title
15
+ = config('ditty.title', 'Ditty')
16
+ - if defined? title
17
+ = "- #{title}"
18
+
19
+ %meta{ name: 'description', content: '' }
20
+ %meta{ name: 'author', content: '' }
21
+
22
+ / Le styles
23
+ %link{ rel: 'stylesheet', href: '/css/sb-admin-2.min.css', media: 'screen' }
24
+ %link{ rel: 'stylesheet', href: '/css/styles.css', media: 'screen' }
25
+ %link{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.2/css/all.min.css', media: 'screen' }
26
+ %body.bg-gradient-primary
27
+ = yield
28
+
29
+ / Placed at the end of the document so the pages load faster
30
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js' }
31
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js' }
32
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.1/jquery.easing.min.js' }
33
+ / %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js' }
34
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js' }
35
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.7/js/select2.min.js' }
36
+ %script{ type: 'text/javascript', src: '/js/sb-admin-2.min.js' }
37
+ %script{ type: 'text/javascript', src: '/js/scripts.js' }
38
+ :javascript
39
+ $(function() {
40
+ $('.select2').select2();
41
+ });
42
+
@@ -1 +1 @@
1
- %h2 Hello! Ready to get this Ditty started?
1
+ %h1.h3.mb-4.text-gray-800 Home Page
@@ -12,7 +12,7 @@
12
12
  %link{ rel: 'mask-icon', href: '/safari-pinned-tab.svg', color: '#5bbad5' }
13
13
 
14
14
  %title
15
- Ditty
15
+ = config('ditty.title', 'Ditty')
16
16
  - if defined? title
17
17
  = "- #{title}"
18
18
 
@@ -20,43 +20,50 @@
20
20
  %meta{ name: 'author', content: '' }
21
21
 
22
22
  / Le styles
23
- %link{ rel: 'stylesheet', href: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css', media: 'screen' }
24
- %link{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/startbootstrap-sb-admin-2/3.3.7+1/css/sb-admin-2.min.css', media: 'screen' }
25
- %link{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/metisMenu/2.5.2/metisMenu.min.css', media: 'screen' }
26
- %link{ rel: 'stylesheet', href: 'https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css', media: 'screen' }
27
- /[if lt IE 9] <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
28
- /[if lt IE 9] <script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
29
-
30
- %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js' }
31
- %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/startbootstrap-sb-admin-2/3.3.7+1/js/sb-admin-2.min.js' }
32
- %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/metisMenu/2.5.2/metisMenu.min.js' }
33
- %body
23
+ %link{ rel: 'stylesheet', href: '/css/sb-admin-2.min.css', media: 'screen' }
24
+ %link{ rel: 'stylesheet', href: '/css/styles.css', media: 'screen' }
25
+ %link{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.2/css/all.min.css', media: 'screen' }
26
+ %body#page-top
27
+ / Page Wrapper
34
28
  #wrapper
35
- = haml :'partials/navbar', locals: { title: (defined?(title) ? title : 'Ditty') }
36
- #page-wrapper
37
- -if defined?(title) || defined?(actions)
38
- .row
39
- %h1.col-md-9= defined?(title) ? title : '&nbsp;'
40
- .col-md-3.text-right{ style: 'margin-top: 20px' }
41
- = haml :'partials/actions', locals: { actions: defined?(actions) ? actions : {} }
42
- -else
43
- %div{ style: 'padding-top: 20px' }
44
-
45
- .row
46
- .col-md-12
29
+ = haml :'partials/sidebar', locals: { title: (defined?(title) ? title : 'Ditty') }
30
+
31
+ #content-wrapper.d-flex.flex-column
32
+ #content
33
+ = haml :'partials/topbar'
34
+ .container-fluid
35
+ / TODO
36
+ / .row
37
+ / .col-md-12
47
38
  = haml :'partials/notifications'
39
+ - if defined?(title) || defined?(actions)
40
+ .row
41
+ .col-md-9
42
+ %h1.text-dark.mb-2= defined?(title) ? title : '&nbsp;'
43
+ .col-md-3.text-right.mt-1
44
+ = haml :'partials/actions', locals: { actions: defined?(actions) ? actions : {} }
45
+ - else
46
+ %div{ style: 'padding-top: 20px' }
47
+
48
+ = yield
48
49
 
49
- = yield
50
50
  = haml :'partials/footer'
51
51
 
52
+ / Scroll to Top Button
53
+ %a.scroll-to-top.rounded{ href: '#page-top' }
54
+ %i.fas.fa-angle-up
55
+
52
56
  / Placed at the end of the document so the pages load faster
53
- %script{ type: 'text/javascript', src: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js' }
54
- %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.min.js' }
55
- %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.bundle.min.js' }
56
- %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js' }
57
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js' }
58
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js' }
59
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.1/jquery.easing.min.js' }
60
+ / %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js' }
61
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js' }
62
+ %script{ type: 'text/javascript', src: 'https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.7/js/select2.min.js' }
63
+ %script{ type: 'text/javascript', src: '/js/sb-admin-2.min.js' }
64
+ %script{ type: 'text/javascript', src: '/js/scripts.js' }
57
65
  :javascript
58
66
  $(function() {
59
- $('.sidebar-nav').metisMenu();
60
67
  $('.select2').select2();
61
68
  });
62
69
 
File without changes
@@ -1,2 +1,2 @@
1
- = delete_form_tag "#{base_path}/#{entity.id}" do
1
+ = delete_form_tag "#{base_path}/#{entity.display_id}" do
2
2
  %button.btn.btn-danger{ type: 'submit' }= delete_label
@@ -1,4 +1,4 @@
1
- .form-group
1
+ .form-group{ class: total_filters.to_i > 4 ? 'col-3' : 'col-md'}
2
2
  %label{ for: "filter_#{name}" }= label
3
3
  %select.form-control{ name: name, id: "filter_#{name}" }
4
4
  %option{ value: '' } -- Select One --
@@ -1,5 +1,5 @@
1
- %footer.footer.text-muted.text-center
2
- %hr
3
- .copyright
4
- :plain
5
- &copy; <script>document.write(new Date().getFullYear())</script>, Ditty
1
+ / Footer
2
+ %footer.sticky-footer.bg-light
3
+ .container.my-auto
4
+ .copyright.text-center.my-auto.text-dark
5
+ %span Copyright &copy; Ditty.io 2019
@@ -1,24 +1,31 @@
1
1
  - type = attributes.delete(:type)
2
+ - attributes[:value] ||= model.send(field) || default
3
+ - if help_text
4
+ - attributes[:'aria-describedby'] = "#{attributes[:id]}_helptext"
2
5
  - if type == 'hidden'
3
- %input{attributes, type: type, value: model[field] || default}
6
+ %input{attributes, type: type, value: model.send(field) || default}
4
7
  - else
5
- %div{ class: "form-group#{model.errors[field] ? ' has-error' : ''}" }
8
+ .form-group
6
9
  - if type != 'file'
7
- %label.col-sm-3.control-label{ for: attributes[:id] }= label
8
- .col-sm-9
10
+ %label{ for: attributes[:id] }= label
9
11
  - if type == 'select'
10
12
  - options = attributes.delete(:options)
13
+ - value = attributes.delete(:value)
14
+ - if attributes[:multiple]
15
+ - attributes[:name] = "#{attributes[:name]}[]" unless attributes[:name][-2..-1] == '[]'
11
16
  %select{attributes}
12
17
  - if attributes[:multiple]
13
- - options.each do |k,v| k ||= v; v ||= k;
14
- %option{ value: k, selected: (model.send(field).map(&:id).include? k)}= v
18
+ - options.each do |k, v| k = v if k.nil?; v = k if v.nil?;
19
+ %option{ value: k.to_s, selected: value&.include?(k) }= v
15
20
  - else
16
- %option{ value: ""} -- Select One --
17
- - options.each do |k,v| k ||= v; v ||= k;
18
- %option{ value: k, selected: [model[field].to_s, default].include?(k.to_s)}= v
21
+ %option{ value: "" } -- Select One --
22
+ - options.each do |k, v| k = v if k.nil?; v = k if v.nil?;
23
+ %option{ value: k.to_s, selected: value == k }= v
19
24
  - elsif type == 'textarea'
20
- %textarea{attributes}= preserve(model[field] || default)
25
+ %textarea{attributes}= preserve(model.send(field) || default)
21
26
  - else
22
- %input{attributes, type: type, value: model[field] || default}
27
+ %input{ attributes, type: type }
23
28
  - if model.errors[field]
24
- %p.help-block.text-danger= model.errors[field].join(', ')
29
+ .invalid-feedback= model.errors[field].join(', ')
30
+ - if help_text
31
+ %small.form-text.text-muted{ id: "#{attributes[:id]}_helptext" }= help_text
@@ -0,0 +1,44 @@
1
+ - if authenticated?
2
+ - Ditty::Components.components.each do |key, comp|
3
+ - next unless comp.respond_to?(:navigation) && comp.navigation.count.positive?
4
+ - if comp.respond_to?(:title)
5
+ .sidebar-heading= comp.title
6
+ - comp.navigation.each do |item|
7
+ - if item[:target].nil? || policy(item[:target]).list?
8
+ - if item[:group]
9
+ %li.nav-item
10
+ %a.nav-link.collapsed{ href: '#', 'data-toggle': 'collapse', 'data-target': "##{item[:group].parameterize}", 'aria-expanded': 'true', 'aria-controls': item[:group].parameterize }
11
+ - if item[:icon]
12
+ %i.fas.fa-fw{ class: "fa-#{item[:icon]}" }
13
+ %span= item[:group]
14
+ .collapse{ id: item[:group].parameterize, 'data-parent': '#accordionSidebar' }
15
+ .bg-white.py-2.collapse-inner.rounded
16
+ - item[:items].each do |sub_item|
17
+ - next unless sub_item[:target] && policy(sub_item[:target]).list?
18
+ %a.collapse-item{ href: "#{settings.map_path}#{sub_item[:link]}" }
19
+ - if sub_item[:icon]
20
+ %i.fa.fa-fw{ class: "fa-#{sub_item[:icon]}" }
21
+ = sub_item[:text]
22
+ - else
23
+ %li.nav-item
24
+ %a.nav-link{ href: "#{settings.map_path}#{item[:link]}" }
25
+ - if item[:icon]
26
+ %i.fas.fa-fw{ class: "fa-#{item[:icon]}" }
27
+ %span= item[:text]
28
+ %hr.sidebar-divider
29
+ %li.nav-item
30
+ = delete_form_tag("#{settings.map_path}/auth", attributes: { id: 'logout-form' }) do
31
+ %a.nav-link{ type: 'submit', onClick: 'document.getElementById("logout-form").submit()' }
32
+ %i.fas.fa-fw.fa-sign-out-alt
33
+ %span Logout
34
+ - else
35
+ %li.nav-item
36
+ %a.nav-link{ href: "#{settings.map_path}/auth/login" }
37
+ %i.fas.fa-fw.fa-user
38
+ %span Log In
39
+ - if policy(::Ditty::User).register?
40
+ %li.nav-item
41
+ %a.nav-link{ href: "#{settings.map_path}/auth/register" }
42
+ %i.fas.fa-fw.fa-pen-square
43
+ %span Register
44
+
@@ -1,28 +1,32 @@
1
1
  - if flash[:danger]
2
2
  .alert.alert-danger.alert-dismissible{ role: "alert" }
3
- %button.close{ 'type' => 'button', 'aria-hidden' => 'true', 'area-label' => 'true', 'data-dismiss' => 'alert' } &times;
4
- %ul
3
+ %button.close{ type: 'button', 'aria-label': 'Close', 'data-dismiss': 'alert' }
4
+ %span{ 'aria-hidden': 'true' } &times;
5
+ %ul.mb-0
5
6
  - [*flash[:danger]].each do |msg|
6
7
  %li= msg
7
8
 
8
9
  - if flash[:warning]
9
10
  .alert.alert-warning.alert-dismissible{ role: "alert" }
10
- %button.close{ 'type' => 'button', 'aria-hidden' => 'true', 'area-label' => 'true', 'data-dismiss' => 'alert' } &times;
11
- %ul
11
+ %button.close{ type: 'button', 'aria-label': 'Close', 'data-dismiss': 'alert' }
12
+ %span{ 'aria-hidden': 'true' } &times;
13
+ %ul.mb-0
12
14
  - [*flash[:warning]].each do |msg|
13
15
  %li= msg
14
16
 
15
17
  - if flash[:success]
16
18
  .alert.alert-success.alert-dismissible{ role: "alert" }
17
- %button.close{ 'type' => 'button', 'aria-hidden' => 'true', 'area-label' => 'true', 'data-dismiss' => 'alert' } &times;
18
- %ul
19
+ %button.close{ type: 'button', 'aria-label': 'Close', 'data-dismiss': 'alert' }
20
+ %span{ 'aria-hidden': 'true' } &times;
21
+ %ul.mb-0
19
22
  - [*flash[:success]].each do |msg|
20
23
  %li= msg
21
24
 
22
25
 
23
26
  - if flash[:info]
24
27
  .alert.alert-info.alert-dismissible{ role: "alert" }
25
- %button.close{ 'type' => 'button', 'aria-hidden' => 'true', 'area-label' => 'true', 'data-dismiss' => 'alert' } &times;
26
- %ul
28
+ %button.close{ type: 'button', 'aria-label': 'Close', 'data-dismiss': 'alert' }
29
+ %span{ 'aria-hidden': 'true' } &times;
30
+ %ul.mb-0
27
31
  - [*flash[:info]].each do |msg|
28
32
  %li= msg
@@ -1,26 +1,26 @@
1
1
  %nav{"aria-label" => "Page navigation"}
2
2
  - if list.pagination_record_count > 0
3
3
  %p.text-center Showing #{list.current_page_record_range} of #{list.pagination_record_count} records
4
- %ul.pager
4
+ %ul.pagination.justify-content-center
5
5
  - if list.first_page?
6
- %li.disabled
7
- %span First
8
- %li.disabled
9
- %span Previous
6
+ %li.page-item.disabled
7
+ %span.page-link First
8
+ %li.page-item.disabled
9
+ %span.page-link Previous
10
10
  - else
11
- %li
12
- %a{href: first_link} First
13
- %li
14
- %a{href: prev_link} Previous
11
+ %li.page-item
12
+ %a.page-link{href: first_link} First
13
+ %li.page-item
14
+ %a.page-link{href: prev_link} Previous
15
15
  - if list.last_page?
16
- %li.disabled
17
- %span Next
18
- %li.disabled
19
- %span Last
16
+ %li.page-item.disabled
17
+ %span.page-link Next
18
+ %li.page-item.disabled
19
+ %span.page-link Last
20
20
  - else
21
- %li
22
- %a{href: next_link} Next
23
- %li
24
- %a{href: last_link} Last
21
+ %li.page-item
22
+ %a.page-link{href: next_link} Next
23
+ %li.page-item
24
+ %a.page-link{href: last_link} Last
25
25
  - else
26
26
  %p.text-center No records to show
@@ -9,13 +9,12 @@
9
9
  %span.fa.fa-search
10
10
  - if self.class.const_defined? :FILTERS
11
11
  .input-group-btn
12
- %button.btn.btn-default{ type: 'button', :'data-toggle' => 'collapse', :'data-target' => '#filter-form', :'aria-expanded' => 'false', :'aria-controls' => '#filter-form'}
12
+ %button.btn.btn-secondary{ type: 'button', :'data-toggle' => 'collapse', :'data-target' => '#filter-form', :'aria-expanded' => 'false', :'aria-controls' => '#filter-form'}
13
13
  %span.fa.fa-arrow-down
14
14
 
15
15
  - if self.class.const_defined?(:FILTERS)
16
- #filter-form{ class: self.class.const_defined?(:SEARCHABLE) ? 'collapse' : '' }
17
- .form-inline
18
- - FILTERS.each do |filter|
19
- = filter_control(filter)
20
- - unless self.class.const_defined?(:SEARCHABLE)
21
- %button.btn.btn-default{ type: 'submit'} Go
16
+ #filter-form.row{ class: self.class.const_defined?(:SEARCHABLE) ? 'collapse' : '' }
17
+ - FILTERS.each do |filter|
18
+ = filter_control(filter, filters: FILTERS.count)
19
+ - unless self.class.const_defined?(:SEARCHABLE)
20
+ %button.btn.btn-secondary{ type: 'submit'} Go
@@ -1,37 +1,15 @@
1
- .sidebar-nav.navbar-collapse
2
- %ul.nav.nav-pills.nav-stacked
3
- - if authenticated?
4
- %li
5
- %a{ href: "#{settings.map_path}/" }
6
- %i.fa.fa-home.fa-fw
7
- Home
8
- - Ditty::Components.navigation.each do |item|
9
- - if item[:target].nil? || policy(item[:target]).list?
10
- - if item[:group]
11
- %li
12
- %a{ href: '#' }
13
- %i.fa.fa-fw{ class: "fa-#{item[:icon]}" }
14
- =item[:group]
15
- %span.fa.arrow
16
- %ul.nav.nav-second-level
17
- - item[:items].each do |sub_item|
18
- - if sub_item[:target].nil? || policy(sub_item[:target]).list?
19
- %li
20
- %a{ href: "#{settings.map_path}#{sub_item[:link]}" }
21
- %i.fa.fa-fw{ class: "fa-#{sub_item[:icon]}" }
22
- = sub_item[:text]
23
- - else
24
- %li
25
- %a{ href: "#{settings.map_path}#{item[:link]}" }
26
- %i.fa.fa-fw{ class: "fa-#{item[:icon]}" }
27
- = item[:text]
28
- - else
29
- %li
30
- %a{ href: "#{settings.map_path}/auth/login" }
31
- %i.fa.fa-user.fa-fw
32
- Log In
33
- - if policy(::Ditty::User).register?
34
- %li
35
- %a{ href: "#{settings.map_path}/auth/register" }
36
- %i.fa.fa-pencil-square-o.fa-fw
37
- Register
1
+ / Sidebar
2
+ %ul#accordionSidebar.navbar-nav.bg-gradient-primary.sidebar.sidebar-dark.accordion
3
+ / Sidebar - Brand
4
+ %a.sidebar-brand.d-flex.align-items-center.justify-content-center{ href: config('ditty.home_page', settings.map_path || '/') }
5
+ .sidebar-brand-icon.rotate-n-15
6
+ %i.fas.fa-laugh-wink
7
+ .sidebar-brand-text.mx-3
8
+ = config('ditty.title', 'Ditty')
9
+
10
+ / Divider
11
+ %hr.sidebar-divider.my-0
12
+ = haml :'partials/navitems'
13
+
14
+ .text-center.d-none.d-md-inline
15
+ %button#sidebarToggle.rounded-circle.border-0
@@ -0,0 +1,68 @@
1
+ / Logout Modal
2
+ #logoutModal.modal.fade{ tabindex: -1, role: 'dialog', 'aria-hidden': 'true' }
3
+ .modal-dialog{ role: 'document' }
4
+ .modal-content
5
+ .modal-header
6
+ %h5.modal-title Ready to Leave?
7
+ %button.close{ type: 'button', 'data-dismiss': 'modal', 'aria-label': 'Close' }
8
+ %span{ 'aria-hidden': 'true' } ×
9
+ .modal-body
10
+ Select "Logout" below if you are ready to end your current session.
11
+ .modal-footer
12
+ %button.btn.btn-secondary{ type: 'button', 'data-dismiss': 'modal' } Cancel
13
+ = delete_form_tag("#{settings.map_path}/auth") do
14
+ %button.btn.btn-primary{ type: 'submit' } Logout
15
+
16
+ %nav.navbar.navbar-expand.navbar-light.bg-white.topbar.mb-4.static-top.shadow
17
+ / Sidebar Toggle
18
+ %button#sidebarToggleTop.btn.btn-link.d-md-none.rounded-circle.mr-3
19
+ %i.fa.fa-bars
20
+
21
+ / Topbar Search
22
+ %form.d-none.d-sm-inline-block.form-inline.mr-auto.ml-md-3.my-2.my-md-0.mw-100.navbar-search
23
+ .input-group
24
+ %input.form-control.bg-light.border-0.small{ type: 'text', placeholder: 'Search for...', 'aria-label': 'Search', 'aria-describedby': 'basic-addon2' }
25
+ .input-group-append
26
+ %button.btn.btn-primary{ type: 'button' }
27
+ %i.fas.fa-search.fa-sm
28
+
29
+ / Topbar Navbar
30
+ %ul.navbar-nav.ml-auto
31
+ / Nav Item - Search Dropdown (Visible Only XS)
32
+ %li.nav-item.dropdown.no-arrow.d-sm-none
33
+ %a#searchDropdown.nav-link.dropdown-toggle{ href: '#', role: 'button', 'data-toggle': 'dropdown', 'aria-haspopup': 'true', 'aria-expanded': 'false' }
34
+ %i.fas.fa-search.fa-fw
35
+ / Dropdown - Messages
36
+ .dropdown-menu.dropdown-menu-right.p-3.shadow.animated--grow-in{ :'aria-labelledby' => 'searchDropdown' }
37
+ %form.form-inline.mr-auto.w-100.navbar-search
38
+ .input-group
39
+ %input.form-control.bg-light.border-0.small{ type: 'text', placeholder: 'Search for...', :'aria-label' => 'Search', :'aria-describedby' => 'basic-addon2' }
40
+ .input-group-append
41
+ %button.btn.btn-primary{ type: 'button' }
42
+ %i.fas.fa-.fa-sm
43
+
44
+
45
+ .topbar-divider.d-none.d-sm-block
46
+
47
+ / Nav Item - User Information
48
+ - if authenticated?
49
+ %li.nav-item.dropdown.no-arrow
50
+ %a#userDropdown.nav-link.dropdown-toggle{ href: '#', role: 'button', 'aria-haspopup': 'true', 'area-expanded': 'false', 'data-toggle': 'dropdown' }
51
+ %span.mr-2.d-none.d-lg-inline.text-gray-600.small= current_user.display_name
52
+ %img.img-profile.rounded-circle{ src: current_user.gravatar }
53
+ / Dropdown - User Information
54
+ .dropdown-menu.dropdown-menu-right.shadow.animated--grow-in{ 'aria-labelledby': 'userDropdown' }
55
+ %a.dropdown-item{ href: "#{settings.map_path}/users/profile" }
56
+ %i.fas.fa-user.fa-sm.fa-fw.mr-2.text-gray-400
57
+ Profile
58
+ / %a.dropdown-item{ href: '#' }
59
+ / %i.fas.fa-cogs.fa-sm.fa-fw.mr-2.text-gray-400
60
+ / Settings
61
+ %a.dropdown-item{ href: "#{settings.map_path}/login-traits" }
62
+ %i.fas.fa-list.fa-sm.fa-fw.mr-2.text-gray-400
63
+ Past Logins
64
+ .dropdown-divider
65
+ %a.dropdown-item{ href: '#', data: { toggle: 'modal', target: '#logoutModal' } }
66
+ %i.fas.fa-sign-out-alt.fa-sm.fa-fw.mr-2.text-gray-400
67
+ Logout
68
+ - else