maquina 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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +8 -0
- data/app/assets/images/maquina/maquina.svg +18 -0
- data/app/assets/javascripts/maquina/application.js +4 -0
- data/app/assets/javascripts/maquina/controllers/alert_controller.js +29 -0
- data/app/assets/javascripts/maquina/controllers/application.js +9 -0
- data/app/assets/javascripts/maquina/controllers/file_controller.js +60 -0
- data/app/assets/javascripts/maquina/controllers/index.js +11 -0
- data/app/assets/javascripts/maquina/controllers/mobile_menu_controller.js +31 -0
- data/app/assets/javascripts/maquina/controllers/modal_controller.js +39 -0
- data/app/assets/javascripts/maquina/controllers/modal_open_controller.js +15 -0
- data/app/assets/javascripts/maquina/controllers/popup_menu_controller.js +17 -0
- data/app/assets/javascripts/maquina/controllers/submit_form_controller.js +11 -0
- data/app/assets/stylesheets/maquina/application.css +15 -0
- data/app/assets/stylesheets/maquina/application.tailwind.css +102 -0
- data/app/controllers/concerns/maquina/authenticate.rb +41 -0
- data/app/controllers/concerns/maquina/create.rb +27 -0
- data/app/controllers/concerns/maquina/destroy.rb +28 -0
- data/app/controllers/concerns/maquina/edit.rb +29 -0
- data/app/controllers/concerns/maquina/index.rb +33 -0
- data/app/controllers/concerns/maquina/new.rb +22 -0
- data/app/controllers/concerns/maquina/resourceful.rb +180 -0
- data/app/controllers/concerns/maquina/show.rb +27 -0
- data/app/controllers/concerns/maquina/update.rb +31 -0
- data/app/controllers/maquina/accept_invitations_controller.rb +28 -0
- data/app/controllers/maquina/application_controller.rb +19 -0
- data/app/controllers/maquina/dashboard_controller.rb +16 -0
- data/app/controllers/maquina/invitations_controller.rb +51 -0
- data/app/controllers/maquina/plans_controller.rb +56 -0
- data/app/controllers/maquina/sessions_controller.rb +56 -0
- data/app/controllers/maquina/unauthorized_controller.rb +9 -0
- data/app/controllers/maquina/users_controller.rb +24 -0
- data/app/helpers/maquina/application_helper.rb +19 -0
- data/app/helpers/maquina/navbar_menu_helper.rb +29 -0
- data/app/helpers/maquina/views_helper.rb +9 -0
- data/app/jobs/maquina/application_job.rb +4 -0
- data/app/mailers/maquina/application_mailer.rb +8 -0
- data/app/mailers/maquina/user_notifications_mailer.rb +16 -0
- data/app/models/concerns/maquina/authenticate_by.rb +33 -0
- data/app/models/concerns/maquina/blockeable.rb +28 -0
- data/app/models/concerns/maquina/multifactor.rb +80 -0
- data/app/models/concerns/maquina/retain_passwords.rb +32 -0
- data/app/models/concerns/maquina/searchable.rb +24 -0
- data/app/models/maquina/active_session.rb +33 -0
- data/app/models/maquina/application_record.rb +11 -0
- data/app/models/maquina/current.rb +21 -0
- data/app/models/maquina/invitation.rb +15 -0
- data/app/models/maquina/plan.rb +38 -0
- data/app/models/maquina/used_password.rb +17 -0
- data/app/models/maquina/user.rb +33 -0
- data/app/policies/maquina/application_policy.rb +50 -0
- data/app/policies/maquina/invitation_policy.rb +13 -0
- data/app/policies/maquina/navigation_policy.rb +13 -0
- data/app/policies/maquina/plan_policy.rb +49 -0
- data/app/policies/maquina/user_policy.rb +27 -0
- data/app/views/layouts/maquina/application.html.erb +26 -0
- data/app/views/layouts/maquina/mailer.html.erb +377 -0
- data/app/views/layouts/maquina/mailer.text.erb +12 -0
- data/app/views/layouts/maquina/sessions.html.erb +24 -0
- data/app/views/maquina/accept_invitations/new.html.erb +9 -0
- data/app/views/maquina/accept_invitations/new_view.rb +41 -0
- data/app/views/maquina/application/_navbar.html.erb +21 -0
- data/app/views/maquina/application/alert.rb +104 -0
- data/app/views/maquina/application/components/action_text_component.rb +20 -0
- data/app/views/maquina/application/components/checkbox_component.rb +21 -0
- data/app/views/maquina/application/components/component_base.rb +60 -0
- data/app/views/maquina/application/components/file_component.rb +59 -0
- data/app/views/maquina/application/components/input_component.rb +20 -0
- data/app/views/maquina/application/components/select_component.rb +44 -0
- data/app/views/maquina/application/create.turbo_stream.erb +11 -0
- data/app/views/maquina/application/edit.html.erb +9 -0
- data/app/views/maquina/application/edit.rb +17 -0
- data/app/views/maquina/application/form.rb +77 -0
- data/app/views/maquina/application/index.html.erb +10 -0
- data/app/views/maquina/application/index_header.rb +46 -0
- data/app/views/maquina/application/index_modal.rb +43 -0
- data/app/views/maquina/application/index_table.rb +121 -0
- data/app/views/maquina/application/new.html.erb +9 -0
- data/app/views/maquina/application/new.rb +18 -0
- data/app/views/maquina/application/sessions_header.rb +31 -0
- data/app/views/maquina/application/show.html.erb +1 -0
- data/app/views/maquina/application/update.turbo_stream.erb +11 -0
- data/app/views/maquina/application_view.rb +46 -0
- data/app/views/maquina/dashboard/index.html.erb +0 -0
- data/app/views/maquina/invitations/create.turbo_stream.erb +13 -0
- data/app/views/maquina/invitations/new.html.erb +3 -0
- data/app/views/maquina/navbar/menu.rb +62 -0
- data/app/views/maquina/navbar/menu_item_link.rb +34 -0
- data/app/views/maquina/navbar/mobile_button.rb +29 -0
- data/app/views/maquina/navbar/mobile_menu.rb +47 -0
- data/app/views/maquina/navbar/notification.rb +37 -0
- data/app/views/maquina/navbar/profile.rb +16 -0
- data/app/views/maquina/navbar/profile_button.rb +24 -0
- data/app/views/maquina/navbar/profile_menu.rb +108 -0
- data/app/views/maquina/navbar/profile_menu_item_link.rb +41 -0
- data/app/views/maquina/navbar/search.rb +40 -0
- data/app/views/maquina/navbar/title.rb +22 -0
- data/app/views/maquina/sessions/create.turbo_stream.erb +11 -0
- data/app/views/maquina/sessions/form.rb +56 -0
- data/app/views/maquina/sessions/new.html.erb +9 -0
- data/app/views/maquina/unauthorized/401.html.erb +1 -0
- data/app/views/maquina/user_notifications_mailer/invitation_email.html.erb +40 -0
- data/app/views/maquina/user_notifications_mailer/invitation_email.text.erb +12 -0
- data/config/definitions.rb +1 -0
- data/config/initializers/importmap.rb +17 -0
- data/config/initializers/money.rb +116 -0
- data/config/initializers/pagy.rb +235 -0
- data/config/locales/flash.en.yml +44 -0
- data/config/locales/forms.en.yml +58 -0
- data/config/locales/mailers.en.yml +35 -0
- data/config/locales/models.en.yml +34 -0
- data/config/locales/routes.en.yml +7 -0
- data/config/locales/views.en.yml +45 -0
- data/config/routes.rb +17 -0
- data/db/migrate/20221109010726_create_maquina_plans.rb +13 -0
- data/db/migrate/20221113000409_create_maquina_users.rb +19 -0
- data/db/migrate/20221113020108_create_maquina_used_passwords.rb +10 -0
- data/db/migrate/20221115223414_create_maquina_active_sessions.rb +15 -0
- data/db/migrate/20230201203922_create_maquina_invitations.rb +12 -0
- data/db/schema.rb +1 -0
- data/lib/generators/maquina/install_generator.rb +32 -0
- data/lib/generators/maquina/install_templates/install_templates_generator.rb +31 -0
- data/lib/generators/maquina/tailwind_config/tailwind_config_generator.rb +11 -0
- data/lib/generators/maquina/tailwind_config/templates/app/assets/config/maquina/tailwind.config.js.tt +68 -0
- data/lib/generators/maquina/templates/config/initializers/maquina.rb +3 -0
- data/lib/maquina/engine.rb +17 -0
- data/lib/maquina/version.rb +3 -0
- data/lib/maquina.rb +48 -0
- data/lib/tasks/install.rake +19 -0
- data/lib/tasks/maquina_tasks.rake +4 -0
- data/lib/tasks/tailwind.rake +25 -0
- 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,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,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,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,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
|