maquina 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +8 -0
  5. data/app/assets/images/maquina/maquina.svg +18 -0
  6. data/app/assets/javascripts/maquina/application.js +4 -0
  7. data/app/assets/javascripts/maquina/controllers/alert_controller.js +29 -0
  8. data/app/assets/javascripts/maquina/controllers/application.js +9 -0
  9. data/app/assets/javascripts/maquina/controllers/file_controller.js +60 -0
  10. data/app/assets/javascripts/maquina/controllers/index.js +11 -0
  11. data/app/assets/javascripts/maquina/controllers/mobile_menu_controller.js +31 -0
  12. data/app/assets/javascripts/maquina/controllers/modal_controller.js +39 -0
  13. data/app/assets/javascripts/maquina/controllers/modal_open_controller.js +15 -0
  14. data/app/assets/javascripts/maquina/controllers/popup_menu_controller.js +17 -0
  15. data/app/assets/javascripts/maquina/controllers/submit_form_controller.js +11 -0
  16. data/app/assets/stylesheets/maquina/application.css +15 -0
  17. data/app/assets/stylesheets/maquina/application.tailwind.css +102 -0
  18. data/app/controllers/concerns/maquina/authenticate.rb +41 -0
  19. data/app/controllers/concerns/maquina/create.rb +27 -0
  20. data/app/controllers/concerns/maquina/destroy.rb +28 -0
  21. data/app/controllers/concerns/maquina/edit.rb +29 -0
  22. data/app/controllers/concerns/maquina/index.rb +33 -0
  23. data/app/controllers/concerns/maquina/new.rb +22 -0
  24. data/app/controllers/concerns/maquina/resourceful.rb +180 -0
  25. data/app/controllers/concerns/maquina/show.rb +27 -0
  26. data/app/controllers/concerns/maquina/update.rb +31 -0
  27. data/app/controllers/maquina/accept_invitations_controller.rb +28 -0
  28. data/app/controllers/maquina/application_controller.rb +19 -0
  29. data/app/controllers/maquina/dashboard_controller.rb +16 -0
  30. data/app/controllers/maquina/invitations_controller.rb +51 -0
  31. data/app/controllers/maquina/plans_controller.rb +56 -0
  32. data/app/controllers/maquina/sessions_controller.rb +56 -0
  33. data/app/controllers/maquina/unauthorized_controller.rb +9 -0
  34. data/app/controllers/maquina/users_controller.rb +24 -0
  35. data/app/helpers/maquina/application_helper.rb +19 -0
  36. data/app/helpers/maquina/navbar_menu_helper.rb +29 -0
  37. data/app/helpers/maquina/views_helper.rb +9 -0
  38. data/app/jobs/maquina/application_job.rb +4 -0
  39. data/app/mailers/maquina/application_mailer.rb +8 -0
  40. data/app/mailers/maquina/user_notifications_mailer.rb +16 -0
  41. data/app/models/concerns/maquina/authenticate_by.rb +33 -0
  42. data/app/models/concerns/maquina/blockeable.rb +28 -0
  43. data/app/models/concerns/maquina/multifactor.rb +80 -0
  44. data/app/models/concerns/maquina/retain_passwords.rb +32 -0
  45. data/app/models/concerns/maquina/searchable.rb +24 -0
  46. data/app/models/maquina/active_session.rb +33 -0
  47. data/app/models/maquina/application_record.rb +11 -0
  48. data/app/models/maquina/current.rb +21 -0
  49. data/app/models/maquina/invitation.rb +15 -0
  50. data/app/models/maquina/plan.rb +38 -0
  51. data/app/models/maquina/used_password.rb +17 -0
  52. data/app/models/maquina/user.rb +33 -0
  53. data/app/policies/maquina/application_policy.rb +50 -0
  54. data/app/policies/maquina/invitation_policy.rb +13 -0
  55. data/app/policies/maquina/navigation_policy.rb +13 -0
  56. data/app/policies/maquina/plan_policy.rb +49 -0
  57. data/app/policies/maquina/user_policy.rb +27 -0
  58. data/app/views/layouts/maquina/application.html.erb +26 -0
  59. data/app/views/layouts/maquina/mailer.html.erb +377 -0
  60. data/app/views/layouts/maquina/mailer.text.erb +12 -0
  61. data/app/views/layouts/maquina/sessions.html.erb +24 -0
  62. data/app/views/maquina/accept_invitations/new.html.erb +9 -0
  63. data/app/views/maquina/accept_invitations/new_view.rb +41 -0
  64. data/app/views/maquina/application/_navbar.html.erb +21 -0
  65. data/app/views/maquina/application/alert.rb +104 -0
  66. data/app/views/maquina/application/components/action_text_component.rb +20 -0
  67. data/app/views/maquina/application/components/checkbox_component.rb +21 -0
  68. data/app/views/maquina/application/components/component_base.rb +60 -0
  69. data/app/views/maquina/application/components/file_component.rb +59 -0
  70. data/app/views/maquina/application/components/input_component.rb +20 -0
  71. data/app/views/maquina/application/components/select_component.rb +44 -0
  72. data/app/views/maquina/application/create.turbo_stream.erb +11 -0
  73. data/app/views/maquina/application/edit.html.erb +9 -0
  74. data/app/views/maquina/application/edit.rb +17 -0
  75. data/app/views/maquina/application/form.rb +77 -0
  76. data/app/views/maquina/application/index.html.erb +10 -0
  77. data/app/views/maquina/application/index_header.rb +46 -0
  78. data/app/views/maquina/application/index_modal.rb +43 -0
  79. data/app/views/maquina/application/index_table.rb +121 -0
  80. data/app/views/maquina/application/new.html.erb +9 -0
  81. data/app/views/maquina/application/new.rb +18 -0
  82. data/app/views/maquina/application/sessions_header.rb +31 -0
  83. data/app/views/maquina/application/show.html.erb +1 -0
  84. data/app/views/maquina/application/update.turbo_stream.erb +11 -0
  85. data/app/views/maquina/application_view.rb +46 -0
  86. data/app/views/maquina/dashboard/index.html.erb +0 -0
  87. data/app/views/maquina/invitations/create.turbo_stream.erb +13 -0
  88. data/app/views/maquina/invitations/new.html.erb +3 -0
  89. data/app/views/maquina/navbar/menu.rb +62 -0
  90. data/app/views/maquina/navbar/menu_item_link.rb +34 -0
  91. data/app/views/maquina/navbar/mobile_button.rb +29 -0
  92. data/app/views/maquina/navbar/mobile_menu.rb +47 -0
  93. data/app/views/maquina/navbar/notification.rb +37 -0
  94. data/app/views/maquina/navbar/profile.rb +16 -0
  95. data/app/views/maquina/navbar/profile_button.rb +24 -0
  96. data/app/views/maquina/navbar/profile_menu.rb +108 -0
  97. data/app/views/maquina/navbar/profile_menu_item_link.rb +41 -0
  98. data/app/views/maquina/navbar/search.rb +40 -0
  99. data/app/views/maquina/navbar/title.rb +22 -0
  100. data/app/views/maquina/sessions/create.turbo_stream.erb +11 -0
  101. data/app/views/maquina/sessions/form.rb +56 -0
  102. data/app/views/maquina/sessions/new.html.erb +9 -0
  103. data/app/views/maquina/unauthorized/401.html.erb +1 -0
  104. data/app/views/maquina/user_notifications_mailer/invitation_email.html.erb +40 -0
  105. data/app/views/maquina/user_notifications_mailer/invitation_email.text.erb +12 -0
  106. data/config/definitions.rb +1 -0
  107. data/config/initializers/importmap.rb +17 -0
  108. data/config/initializers/money.rb +116 -0
  109. data/config/initializers/pagy.rb +235 -0
  110. data/config/locales/flash.en.yml +44 -0
  111. data/config/locales/forms.en.yml +58 -0
  112. data/config/locales/mailers.en.yml +35 -0
  113. data/config/locales/models.en.yml +34 -0
  114. data/config/locales/routes.en.yml +7 -0
  115. data/config/locales/views.en.yml +45 -0
  116. data/config/routes.rb +17 -0
  117. data/db/migrate/20221109010726_create_maquina_plans.rb +13 -0
  118. data/db/migrate/20221113000409_create_maquina_users.rb +19 -0
  119. data/db/migrate/20221113020108_create_maquina_used_passwords.rb +10 -0
  120. data/db/migrate/20221115223414_create_maquina_active_sessions.rb +15 -0
  121. data/db/migrate/20230201203922_create_maquina_invitations.rb +12 -0
  122. data/db/schema.rb +1 -0
  123. data/lib/generators/maquina/install_generator.rb +32 -0
  124. data/lib/generators/maquina/install_templates/install_templates_generator.rb +31 -0
  125. data/lib/generators/maquina/tailwind_config/tailwind_config_generator.rb +11 -0
  126. data/lib/generators/maquina/tailwind_config/templates/app/assets/config/maquina/tailwind.config.js.tt +68 -0
  127. data/lib/generators/maquina/templates/config/initializers/maquina.rb +3 -0
  128. data/lib/maquina/engine.rb +17 -0
  129. data/lib/maquina/version.rb +3 -0
  130. data/lib/maquina.rb +48 -0
  131. data/lib/tasks/install.rake +19 -0
  132. data/lib/tasks/maquina_tasks.rake +4 -0
  133. data/lib/tasks/tailwind.rake +25 -0
  134. metadata +456 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e5dffb067d8cdaf6d56c9777275907b321ec48188950945521a44d4d48fd92a2
4
+ data.tar.gz: 28a3e313221bd3f2e465ba565e5e493ba3ccc419512c7f5f59d80353a0d45168
5
+ SHA512:
6
+ metadata.gz: 2b4517a5605cd8245948afec893c75b60fdc522d56982d76928726fdcf797e2552a026321f2e24d2ab8a7387e8de01158a10065300d4914f5d07cf4b1df0c3d7
7
+ data.tar.gz: cb442f6712c58691771d8b01f99d22038c9491b92c5cf324860d5e250b692d749c67254184194de8d2b0132fb9339d32f17ebe6976c9eacd30a903a87bf098a7
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2022 Mario Alberto Chávez
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # Maquina
2
+ Short description and motivation.
3
+
4
+ ## Usage
5
+ How to use my plugin.
6
+
7
+ ## Installation
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem "maquina"
12
+ ```
13
+
14
+ And then execute:
15
+ ```bash
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+ ```bash
21
+ $ gem install maquina
22
+ ```
23
+
24
+ ## Contributing
25
+ Contribution directions go here.
26
+
27
+ ## License
28
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
@@ -0,0 +1,18 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg version="1.1" viewBox="0 0 486 486" xmlns="http://www.w3.org/2000/svg">
3
+ <title>Maquina</title>
4
+ <defs>
5
+ <linearGradient id="a" x1="21.152%" y1="21.848%" y2="93.468%">
6
+ <stop stop-color="#50B1FD" stop-opacity=".96" offset="0"/>
7
+ <stop stop-color="#DE77DE" stop-opacity=".96" offset="1"/>
8
+ </linearGradient>
9
+ </defs>
10
+ <g fill="none" fill-rule="evenodd">
11
+ <g transform="translate(-13 -13)">
12
+ <g transform="translate(13 13)">
13
+ <circle cx="243" cy="243" r="243" fill="url(#a)"/>
14
+ <path d="m238.9 47.002 3.4301 0.016598v30.431c-108.78 8.7223-163.18 71.895-163.18 189.52l54.567-31.709c3.7144-56.281 50.542-100.78 107.76-100.78 10.162 0 19.996 1.4034 29.319 4.0271 0.78425-57.904 1.3072-88.259 1.5685-91.068 161.58 36.443 197.13 192.94 138.54 291.1-4.3817-2.5298-13.063-7.5417-26.354-15.216 46.838-98.57 19.325-177.26-82.539-236.07l0.18734 65.903c28.537 19.437 47.276 52.193 47.276 89.325 0 30.25-12.436 57.595-32.475 77.201 49.541 28.346 75.468 43.309 77.781 44.887-110.21 123.65-264.32 78.878-321.77-19.957 4.3369-2.6059 12.929-7.7686 26.085-15.673 63.504 88.753 145.67 102.84 246.49 42.26l-57.118-31.449c-14.207 6.876-30.149 10.731-46.991 10.731-50.271 0-92.52-34.347-104.56-80.855-49.09 27.458-74.899 41.768-77.424 42.929-48.243-154.99 63.879-262.81 175.97-265.5l3.4312-0.04945zm3.0839 149.47c-25.681 0-46.5 20.819-46.5 46.5s20.819 46.5 46.5 46.5c25.681 0 46.5-20.819 46.5-46.5s-20.819-46.5-46.5-46.5z" fill="#fff"/>
15
+ </g>
16
+ </g>
17
+ </g>
18
+ </svg>
@@ -0,0 +1,4 @@
1
+ // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
2
+ import "@hotwired/turbo-rails"
3
+
4
+ import "controllers"
@@ -0,0 +1,29 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+ import { useTransition } from "stimulus-use"
3
+
4
+ /**
5
+ * Alert Controller
6
+ *
7
+ * Cuando hay información en el objeto Flash de Rails se muestra con una animación
8
+ * el mensaje y desaparece solo después de 10 segundos o cerrándolo con la X.
9
+ */
10
+
11
+ export default class extends Controller {
12
+ static targets = ["alert"];
13
+
14
+ connect() {
15
+ let element = this.element;
16
+ if (this.hasAlertTarget) {
17
+ element = this.alertTarget
18
+ }
19
+ useTransition(this, { element: element })
20
+
21
+ this.enter()
22
+
23
+ setTimeout(this.close.bind(this), 10000)
24
+ }
25
+
26
+ close() {
27
+ this.leave()
28
+ }
29
+ }
@@ -0,0 +1,9 @@
1
+ import { Application } from "@hotwired/stimulus"
2
+
3
+ const application = Application.start()
4
+
5
+ // Configure Stimulus development experience
6
+ application.debug = false
7
+ window.Stimulus = application
8
+
9
+ export { application }
@@ -0,0 +1,60 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ static targets = ["fileInput", "container", "preview", "placeholder"]
5
+ static values = { maxSize: Number, image: String, validationMessage: String }
6
+
7
+ connect() {
8
+ if (this.hasFileInputTarget) {
9
+ this.fileInputTarget.addEventListener("change", this.fileSelected.bind(this))
10
+ }
11
+ }
12
+
13
+ disconnect() {
14
+ if (this.hasFileInputTarget) {
15
+ this.fileInputTarget.removeEventListener("change", this.fileSelected.bind(this))
16
+ }
17
+ }
18
+
19
+ select(event) {
20
+ event.preventDefault();
21
+ this.fileInputTarget.click();
22
+ }
23
+
24
+ fileSelected(event) {
25
+ if (event.target.files.length > 0) {
26
+ this.containerTarget.querySelectorAll(".help-error").forEach(error => error.remove())
27
+ this.validateFile(event.target.files[0])
28
+ }
29
+ }
30
+
31
+ validateFile(file) {
32
+ if (file.size > this.maxSizeValue) {
33
+ this.fileInputTarget.value = ""
34
+ this.addError()
35
+ this.clearPreview()
36
+ } else {
37
+ this.loadPreview(file)
38
+ }
39
+ }
40
+
41
+ addError() {
42
+ const messageNode = document.createElement("p");
43
+ messageNode.classList.add("help", "help-error");
44
+ messageNode.innerText = this.validationMessageValue;
45
+
46
+ this.containerTarget.appendChild(messageNode)
47
+ }
48
+
49
+ clearPreview() {
50
+ this.previewTarget.classList.add("hidden")
51
+ this.placeholderTarget.classList.remove("hidden")
52
+ this.previewTarget.setAttribute("src", "")
53
+ }
54
+
55
+ loadPreview(file) {
56
+ this.previewTarget.setAttribute("src", URL.createObjectURL(file))
57
+ this.previewTarget.classList.remove("hidden")
58
+ this.placeholderTarget.classList.add("hidden")
59
+ }
60
+ }
@@ -0,0 +1,11 @@
1
+ // Import and register all your controllers from the importmap under controllers/*
2
+
3
+ import { application } from "controllers/application"
4
+
5
+ // Eager load all controllers defined in the import map under controllers/**/*_controller
6
+ import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
7
+ eagerLoadControllersFrom("controllers", application)
8
+
9
+ // Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!)
10
+ // import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
11
+ // lazyLoadControllersFrom("controllers", application)
@@ -0,0 +1,31 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+ import { useClickOutside, useTransition } from "stimulus-use"
3
+
4
+ export default class extends Controller {
5
+ static targets = ["menu", "open", "close"]
6
+
7
+ connect() {
8
+ useClickOutside(this);
9
+ useTransition(this, { element: this.menuTarget })
10
+ }
11
+
12
+ toggle() {
13
+ this.toggleTransition();
14
+ }
15
+
16
+ clickOutside() {
17
+ if (!this.menuTarget.classList.contains("hidden")) {
18
+ this.toggleTransition();
19
+ }
20
+ }
21
+
22
+ enter() {
23
+ this.openTarget.classList.toggle("hidden")
24
+ this.closeTarget.classList.toggle("hidden")
25
+ }
26
+
27
+ leave() {
28
+ this.openTarget.classList.toggle("hidden")
29
+ this.closeTarget.classList.toggle("hidden")
30
+ }
31
+ }
@@ -0,0 +1,39 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+ import { useTransition } from "stimulus-use"
3
+
4
+ export default class extends Controller {
5
+ static targets = ["content", "container", "frame"]
6
+
7
+ connect() {
8
+ useTransition(this, { element: this.contentTarget })
9
+
10
+ this.resultCallback = this.modalResult.bind(this)
11
+ addEventListener("turbo:submit-end", this.resultCallback)
12
+ }
13
+
14
+ disconnect() {
15
+ removeEventListener("turbo:submit-end", this.resultCallback)
16
+ }
17
+
18
+ modalResult(event) {
19
+ if (event.detail.fetchResponse.response.status == 202) {
20
+ this.toggleModal(event)
21
+ }
22
+ }
23
+
24
+ toggleModal(event) {
25
+ event.preventDefault()
26
+
27
+ if (this.hasFrameTarget && this.contentTarget.classList.contains("hidden")) {
28
+ let url = event["detail"]["src"] || this.element.dataset["frameSrc"]
29
+ this.frameTarget.setAttribute("src", url)
30
+ } else if (!this.contentTarget.classList.contains("hidden")) {
31
+ this.frameTarget.setAttribute("src", "")
32
+ this.frameTarget.innerText = ""
33
+ }
34
+
35
+ document.documentElement.classList.toggle("overflow-hidden")
36
+ this.containerTarget.classList.toggle("hidden")
37
+ this.toggleTransition()
38
+ }
39
+ }
@@ -0,0 +1,15 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+ import { useDispatch } from "stimulus-use";
3
+
4
+ export default class extends Controller {
5
+ static targets = [];
6
+
7
+ connect() {
8
+ useDispatch(this)
9
+ }
10
+
11
+ open(event) {
12
+ event.preventDefault();
13
+ this.dispatch("toggle", {src: event.target.getAttribute("href")});
14
+ }
15
+ }
@@ -0,0 +1,17 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+ import { useClickOutside, useTransition } from "stimulus-use"
3
+
4
+ export default class extends Controller {
5
+ static targets = ["menu"];
6
+
7
+ connect() {
8
+ useClickOutside(this);
9
+ useTransition(this, { element: this.menuTarget })
10
+ }
11
+
12
+ clickOutside() {
13
+ if (!this.menuTarget.classList.contains("hidden")) {
14
+ this.toggleTransition();
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,11 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ static targets = []
5
+
6
+ clear(event) {
7
+ if (!event.target.value) {
8
+ this.element.submit()
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,102 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ /* @layer base { */
6
+ :root {
7
+ --color-base: 31 27 54;
8
+ --color-accented: 37 99 235;
9
+ --color-inverted: 255 255 255;
10
+ --color-accented-hover: 59 130 246;
11
+ --color-muted: 55 65 81;
12
+ --color-dimmed: 75 85 99;
13
+ --color-error: 220 38 38;
14
+
15
+ --color-border-base: 209 213 219;
16
+ --color-border-accented: 37 99 235;
17
+ }
18
+
19
+ .label {
20
+ @apply text-sm font-medium text-skin-muted;
21
+ }
22
+
23
+ .input {
24
+ @apply appearance-none rounded-md border border-skin-base px-3 py-2 placeholder-gray-400 shadow-sm focus:border-skin-accented focus:outline-none focus:ring-skin-accented sm:text-sm;
25
+ }
26
+
27
+ .link {
28
+ @apply font-medium text-skin-accented hover:text-skin-accented-hover;
29
+ }
30
+
31
+ .check {
32
+ @apply border-skin-base h-4 w-4 text-skin-accented focus:ring-skin-accented rounded;
33
+ }
34
+
35
+ .button {
36
+ @apply inline-flex justify-center py-2 px-4 border border-skin-base rounded-md shadow-sm text-sm font-medium text-skin-muted bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-skin-accented;
37
+ }
38
+
39
+ .control-error .label {
40
+ @apply text-skin-error;
41
+ }
42
+
43
+ .control-error .input {
44
+ @apply border-skin-error focus:border-skin-error focus:ring-skin-error;
45
+ }
46
+
47
+ .help {
48
+ @apply mt-2 text-sm text-skin-dimmed;
49
+ }
50
+
51
+ .help-error {
52
+ @apply text-skin-error;
53
+ }
54
+
55
+ .button[disabled] {
56
+ @apply bg-gray-50 text-skin-dimmed cursor-default;
57
+ }
58
+
59
+ .button-accented {
60
+ @apply rounded-md border border-transparent bg-skin-accented py-2 px-4 text-sm font-medium text-skin-inverted shadow-sm hover:bg-skin-accented-hover focus:outline-none focus:ring-2 focus:ring-skin-accented focus:ring-offset-2;
61
+ }
62
+
63
+ input[type="search"] {
64
+ @apply block w-full rounded-md border border-skin-base pl-10 leading-5 placeholder-gray-400 focus:placeholder-gray-300 focus:outline-none focus:ring-1 focus:ring-skin-accented focus:border-skin-accented sm:text-sm;
65
+ }
66
+
67
+ .desktop-menu {
68
+ @apply hidden lg:ml-6 lg:flex lg:space-x-8;
69
+ }
70
+
71
+ .desktop-menu-item {
72
+ @apply font-sans uppercase border-transparent text-skin-dimmed underline-offset-4 decoration-2 hover:underline hover:decoration-skin-accented hover:text-skin-muted inline-flex items-center px-1 pt-1 text-sm font-medium;
73
+ }
74
+
75
+ .desktop-menu-item__active {
76
+ @apply decoration-skin-accented underline text-skin-base;
77
+ }
78
+
79
+ .mobile-menu {
80
+ @apply pt-2 pb-3 space-y-1;
81
+ }
82
+
83
+ .mobile-menu-item {
84
+ @apply font-sans border-transparent text-skin-dimmed hover:bg-gray-200 hover:border-gray-300 hover:text-skin-muted block pl-3 pr-4 py-2 border-l-4 text-base font-medium;
85
+ }
86
+
87
+ .mobile-menu-item__active {
88
+ @apply bg-skin-accented-hover border-skin-accented text-skin-accented hover:text-skin-accented hover:bg-skin-accented-hover hover:border-skin-accented;
89
+ }
90
+
91
+ .desktop-profile-menu-item {
92
+ @apply block w-full text-left font-sans px-4 py-2 text-sm text-skin-dimmed hover:bg-gray-200 hover:text-skin-muted;
93
+ }
94
+
95
+ .mobile-profile-menu-item {
96
+ @apply block font-sans px-4 py-2 text-base font-medium text-skin-dimmed hover:text-skin-muted hover:bg-gray-200;
97
+ }
98
+
99
+ .mobile-button {
100
+ @apply inline-flex items-center justify-center p-2 rounded-md text-skin-dimmed hover:text-skin-muted hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-skin-accented;
101
+ }
102
+ /* } */
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Authenticate
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ helper_method :signed_in?, :current_user
9
+ end
10
+
11
+ def authenticate!
12
+ load_session
13
+
14
+ redirect_to maquina.new_sessions_path if !signed_in?
15
+ end
16
+
17
+ def signed_in?
18
+ load_session if Maquina::Current.active_session.blank?
19
+
20
+ Maquina::Current.signed_in?
21
+ end
22
+
23
+ def current_user
24
+ load_session if Maquina::Current.active_session.blank?
25
+
26
+ Maquina::Current.user
27
+ end
28
+
29
+ private
30
+
31
+ def load_session
32
+ active_session_id = session["--active_session"]
33
+ return if active_session_id.blank?
34
+
35
+ active_session = ActiveSession.eager_load(:user).find_by(id: active_session_id)
36
+ return if active_session.blank?
37
+
38
+ Maquina::Current.active_session = active_session
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Create
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ def create(&block)
9
+ authorize! with: policy_class if policy_class.present?
10
+
11
+ @resource ||= begin
12
+ resource = resource_class.new(resource_secure_params)
13
+ resource.save
14
+
15
+ resource
16
+ end
17
+
18
+ @status = @resource.errors.empty? ? :created : :unprocessable_entity
19
+ response.status = @status
20
+ set_flash_message(@status)
21
+
22
+ dual_action_response(@resource, &block)
23
+ end
24
+ alias_method :create!, :create
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Destroy
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ def destroy(&block)
9
+ @resource ||= begin
10
+ scope = resource_class
11
+ # TODO: Implement policy authorization (ActionPolicy)
12
+ # scope = scope.where(organization)
13
+ # TODO: Implement filtering by organization
14
+ scope.find_by!(find_by_param => params[:id])
15
+ end
16
+
17
+ @resource.destroy
18
+
19
+ status = :no_content
20
+ response.status = status
21
+ set_flash_message(status)
22
+
23
+ dual_action_response(@resource, &block)
24
+ end
25
+ alias_method :destroy!, :destroy
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Edit
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ def edit(&block)
9
+ @resource ||= begin
10
+ scope = resource_class
11
+ # TODO: Implement filtering by organization
12
+ # scope = scope.where(organization)
13
+ scope = yield(scope) if block.present?
14
+
15
+ resource = scope.find_by!(find_by_param => params[:id])
16
+
17
+ authorize! resource, with: policy_class if policy_class.present?
18
+
19
+ resource
20
+ end
21
+
22
+ respond_to do |format|
23
+ format.html
24
+ end
25
+ end
26
+ alias_method :edit!, :edit
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Index
5
+ extend ActiveSupport::Concern
6
+ include Pagy::Backend
7
+
8
+ included do
9
+ def index(&block)
10
+ authorize! with: policy_class if policy_class.present?
11
+
12
+ @collection ||= begin
13
+ scope = resource_class.order(updated_at: :desc)
14
+ scope = authorized_scope(scope) if policy_class.present?
15
+
16
+ search_value = params[:q]&.strip
17
+ scope = scope.search_full(search_value) if search_value.present?
18
+
19
+ scope = yield(scope) if block.present?
20
+
21
+ @pagination, record = pagy(scope)
22
+ record
23
+ end
24
+
25
+ respond_to do |format|
26
+ format.html { render :index }
27
+ format.json { render json: collection }
28
+ end
29
+ end
30
+ alias_method :index!, :index
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module New
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ def new(&block)
9
+ authorize! with: policy_class if policy_class.present?
10
+
11
+ # TODO: Implement filtering by organization
12
+ @resource = resource_class.new
13
+ @resource = yield(@resource) if block.present?
14
+
15
+ respond_to do |format|
16
+ format.html
17
+ end
18
+ end
19
+ alias_method :new!, :new
20
+ end
21
+ end
22
+ end