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
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Navbar
5
+ class MenuItemLink < Phlex::HTML
6
+ include ApplicationView
7
+ delegate :t, :active_menu_option?, to: :helpers
8
+
9
+ def initialize(option, path, desktop: true, active: false)
10
+ @option = option
11
+ @path = path
12
+ @active = active
13
+ @desktop = desktop
14
+ end
15
+
16
+ def template
17
+ a(href: @path, **classes("#{link_type}-menu-item", active?: "#{link_type}-menu-item__active")) { t("menu.main.#{@option}") }
18
+ end
19
+
20
+ private
21
+
22
+ def active?
23
+ active_menu_option?(@path)
24
+ rescue NoMethodError => ex
25
+ Rails.logger.error "[#{self.class}] Please implement helper method :active_menu_option?(path) :: #{ex.message}"
26
+ false
27
+ end
28
+
29
+ def link_type
30
+ @desktop ? "desktop" : "mobile"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Navbar
5
+ class MobileButton < Phlex::HTML
6
+ include ApplicationView
7
+
8
+ def template(&block)
9
+ div class: "flex items-center lg:hidden" do
10
+ button type: "button", class: "mobile-button", "aria-controls": "mobile-menu", "aria-expanded": false, "data-action": "mobile-menu#toggle" do
11
+ span(class: "sr-only") { "Open main menu" }
12
+ svg_icon(:outline, data: {"mobile-menu-target": :open}, icon: closed_icon)
13
+ svg_icon(:outline, css_class: "hidden h-6 -w6", data: {"mobile-menu-target": :close}, icon: opened_icon)
14
+ end
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def closed_icon
21
+ "M4 6h16M4 12h16M4 18h16"
22
+ end
23
+
24
+ def opened_icon
25
+ "M6 18L18 6M6 6l12 12"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Navbar
5
+ class MobileMenu < Phlex::HTML
6
+ include ApplicationView
7
+
8
+ def template
9
+ div class: "hidden lg:hidden", **data_attributes do
10
+ render Maquina::Navbar::Menu.new(desktop: false)
11
+
12
+ div class: "pt-4 pb-3 border-t border-gray-200" do
13
+ div class: "flex items-center px-4" do
14
+ div class: "flex-shrink-0" do
15
+ img class: "h-10 w-10 rounded-full", src: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80", alt: ""
16
+ end
17
+
18
+ div class: "ml-3" do
19
+ div(class: "text-base font-medium text-skin-muted") { "Tom Cook" }
20
+ div(class: "text-sm font-medium text-skin-dimmed") { "tom@example.com" }
21
+ end
22
+
23
+ render Maquina::Navbar::Notification.new(desktop: false)
24
+ end
25
+ render Maquina::Navbar::ProfileMenu.new(desktop: false)
26
+ end
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def data_attributes
33
+ {
34
+ data: {
35
+ "mobile-menu-target": "menu",
36
+ "transition-enter": "transition ease-out duration-100",
37
+ "transition-enter-active": "transform opacity-0 scale-95",
38
+ "transition-enter-to": "transform opacity-100 scale-100",
39
+ "transition-leave": "transition ease-in duration-75",
40
+ "transition-leave-active": "transform opacity-100 scale-100",
41
+ "transition-leave-to": "transform opacity-0 scale-95"
42
+ }
43
+ }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Navbar
5
+ class Notification < Phlex::HTML
6
+ include ApplicationView
7
+
8
+ def initialize(desktop: true)
9
+ @desktop = desktop
10
+ end
11
+
12
+ def template(&block)
13
+ button(**button_attributes) do
14
+ span(class: "sr-only") { "View notifications" }
15
+ svg_icon(:fill, icon: notification_icon)
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def notification_icon
22
+ "M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"
23
+ end
24
+
25
+ def button_attributes
26
+ Rails.logger.warn "[#{self.class}] Notification is hidden"
27
+ attrs = {type: "button"}
28
+
29
+ # Hidden by default
30
+ # attrs[:class] = @desktop ? "hidden lg:block notification-icon" : "ml-auto notification-icon"
31
+ attrs[:class] = @desktop ? "hidden notification-icon" : "hidden ml-auto notification-icon"
32
+
33
+ attrs
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Navbar
5
+ class Profile < Phlex::HTML
6
+ include ApplicationView
7
+
8
+ def template
9
+ div class: "ml-4 relative flex-shrink-0", "data-controller": "popup-menu" do
10
+ render Maquina::Navbar::ProfileButton.new
11
+ render Maquina::Navbar::ProfileMenu.new
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Navbar
5
+ class ProfileButton < Phlex::HTML
6
+ include ApplicationView
7
+
8
+ def template
9
+ div do
10
+ button type: "button", class: "bg-white rounded-full hidden lg:flex text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500", id: "user-menu-button", "aria-expanded": false, "aria-haspopup": true, "data-action": "popup-menu#toggleTransition" do
11
+ span(class: "sr-only") { "Open user menu" }
12
+ svg_icon(:outline, stroke_width: 1.5, icon: user_icon)
13
+ end
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def user_icon
20
+ "M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Navbar
5
+ # == Desktop profile menu at Navbar
6
+ #
7
+ # Renders dropdown profile menu on desktop.
8
+ #
9
+ # Requires Rails Helper method to get access to options for this menu. Method `profile_menu_options` example:
10
+ #
11
+ # def profile_menu_options
12
+ # if Current.user.present?
13
+ # {
14
+ # profile: profiles_path
15
+ # signout: { method: :delete, path: session_path }
16
+ # }
17
+ # else
18
+ # {
19
+ # signin: new_session_path
20
+ # }
21
+ # end
22
+ # end
23
+ #
24
+ # `ProfileMenu` uses I18n to translate menu options. Translation example:
25
+ #
26
+ # menu:
27
+ # profile:
28
+ # profile: My profile
29
+ # signout: Close session
30
+ # signin: Login
31
+ #
32
+ # `ProfileMenu` is initialized by default to render links for desktop. To render mobile links,
33
+ # initialize `ProfileMenu` as follow:
34
+ #
35
+ # Views::Navbar::ProfileMenu.new(desktop: false)
36
+ #
37
+ class ProfileMenu < Phlex::HTML
38
+ include ApplicationView
39
+ delegate :t, :profile_menu_options, to: :helpers
40
+
41
+ def initialize(desktop: true)
42
+ @desktop = desktop
43
+ end
44
+
45
+ def template
46
+ div(**menu_attributes) do
47
+ secure_menu_options.each_pair do |option, path|
48
+ link_path, attributes = path_options(path)
49
+ render Maquina::Navbar::ProfileMenuItemLink.new(option, link_path, attributes, desktop: @desktop)
50
+ end
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def path_options(options)
57
+ attrs = {}
58
+ path = ""
59
+
60
+ if options.is_a?(Hash)
61
+ path = options[:path]
62
+ attrs[:method] = options[:method]
63
+ else
64
+ path = options
65
+ end
66
+
67
+ [path, attrs]
68
+ end
69
+
70
+ def secure_menu_options
71
+ profile_menu_options
72
+ rescue NoMethodError => ex
73
+ Rails.logger.error "[#{self.class}] Please implement helper method :profile_menu_options :: #{ex.message}"
74
+ {}
75
+ end
76
+
77
+ def menu_attributes
78
+ attrs = {class: "mt-3 space-y-1"}
79
+
80
+ if @desktop
81
+ attrs = {
82
+ class: "hidden origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none",
83
+ role: "menu",
84
+ "aria-orientation": "vertical",
85
+ "aria-labelledby": "user-menu-button",
86
+ tabindex: "-1"
87
+ }.merge(**data_attributes)
88
+ end
89
+
90
+ attrs
91
+ end
92
+
93
+ def data_attributes
94
+ {
95
+ data: {
96
+ "popup-menu-target": "menu",
97
+ "transition-enter": "transition ease-out duration-100",
98
+ "transition-enter-active": "transform opacity-0 scale-95",
99
+ "transition-enter-to": "transform opacity-100 scale-100",
100
+ "transition-leave": "transition ease-in duration-75",
101
+ "transition-leave-active": "transform opacity-100 scale-100",
102
+ "transition-leave-to": "transform opacity-0 scale-95"
103
+ }
104
+ }
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Navbar
5
+ class ProfileMenuItemLink < Phlex::HTML
6
+ include ApplicationView
7
+ delegate :t, to: :helpers
8
+
9
+ def initialize(option, path, attributes, desktop: true, active: false)
10
+ @option = option
11
+ @path = path
12
+ @attributes = attributes
13
+ @desktop = desktop
14
+ @active = active
15
+ end
16
+
17
+ def template
18
+ link_method = link_attributes.dig(:method)
19
+ if link_method == :delete
20
+ button_to t("menu.profile.#{@option}"), @path, method: :delete, **link_attributes
21
+ else
22
+ a(href: @path, **link_attributes) { t("menu.profile.#{@option}") }
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def link_attributes
29
+ attrs = {
30
+ role: "menuitem",
31
+ tabindex: "-1"
32
+ }
33
+
34
+ attrs[:class] = @desktop ? "desktop-profile-menu-item" : "mobile-profile-menu-item"
35
+
36
+ attrs.merge!(@attributes) if @attributes.is_a?(Hash) && @attributes.any?
37
+ attrs
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Navbar
5
+ class Search < Phlex::HTML
6
+ include ApplicationView
7
+ include Phlex::Rails::Helpers::FormWith
8
+
9
+ delegate :t, to: :helpers
10
+
11
+ def initialize(url: nil, query: "")
12
+ @url = url
13
+ @query = query
14
+ end
15
+
16
+ def template
17
+ div class: "flex-1 flex items-center justify-center px-2 lg:ml-6 lg:justify-end" do
18
+ div class: "max-w-lg w-full lg:max-w-xs" do
19
+ form_with(url: @url, method: :get, data: {controller: "submit-form"}) do |form|
20
+ form.label :search, t("search.search"), class: "sr-only"
21
+
22
+ div class: "relative" do
23
+ div class: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none" do
24
+ svg_icon(:fill, view_box: "0 0 20 20", css_class: "h-5 w-5 text-skin-dimmed", icon: search_icon)
25
+ end
26
+ form.text_field :q, value: @query, placeholder: t("search.search"), class: "block w-full font-sans", maxlength: 15, type: :search, data: {action: "input->submit-form#clear"}
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def search_icon
36
+ "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Navbar
5
+ class Title < Phlex::HTML
6
+ include Maquina::ApplicationView
7
+
8
+ def initialize(brand_icon: "")
9
+ @brand_icon = brand_icon
10
+ end
11
+
12
+ def template
13
+ div class: "flex-shrink-0 flex items-center" do
14
+ if @brand_icon.present?
15
+ img(src: @brand_icon, class: "hidden lg:block mx-auto h-8 w-auto")
16
+ img(src: @brand_icon, class: "block lg:hidden mx-auto h-8 w-auto")
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ <%= turbo_stream.replace :form do %>
2
+ <%= turbo_frame_tag :form do %>
3
+ <%= render Maquina::Sessions::Form.new(OpenStruct.new(params.slice(:email, :password, :return_to))) %>
4
+ <% end %>
5
+ <% end %>
6
+
7
+ <%= turbo_stream.replace :alert do %>
8
+ <%= turbo_frame_tag :alert do %>
9
+ <%= render Maquina::Application::Alert.new(flash) %>
10
+ <% end %>
11
+ <% end %>
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Sessions
5
+ class Form < Phlex::HTML
6
+ include Maquina::ApplicationView
7
+ include Phlex::Rails::Helpers::FormWith
8
+
9
+ def initialize(resource)
10
+ @resource = resource
11
+ end
12
+
13
+ def template
14
+ div(class: "mt-8 sm:mx-auto sm:w-full sm:max-w-md") do
15
+ div(class: "bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10") do
16
+ form_with(url: sessions_path, method: :post, class: "space-y-6", data: {turbo_frame: :_top, "turbo-cache": false}) do |form|
17
+ form.hidden_field :return_to, value: @resource.return_to
18
+ div do
19
+ form.label t("form.sessions.email"), for: :email, class: "block label"
20
+ div(class: "mt-1") do
21
+ form.text_field :email, value: @resource.email, type: :email, required: true, class: "w-full block input", **field_attributes(:email)
22
+ end
23
+ end
24
+ div do
25
+ form.label t("form.sessions.password"), for: :password, class: "block label"
26
+ div(class: "mt-1") do
27
+ form.password_field :password, value: @resource.password, required: true, class: "w-full block input", **field_attributes(:password)
28
+ end
29
+ end
30
+ div(class: "flex items-center justify-between") do
31
+ div do
32
+ end
33
+ div(class: "text-sm") do
34
+ a(href: "#", class: "link", data: {turbo_frame: :_top}) { t("form.sessions.forgot_password") }
35
+ end
36
+ end
37
+ div do
38
+ form.submit t("form.sessions.submit"), class: "flex w-full justify-center button button-accented"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def field_attributes(field_name, scope = nil)
48
+ scope ||= "sessions"
49
+ {
50
+ maxlength: t("maxlength.#{scope}.#{field_name}", default: t("maxlength.default")),
51
+ placeholder: t("placeholder.#{scope}.#{field_name}", default: "")
52
+ }
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,9 @@
1
+ <div class="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
2
+
3
+ <%= render Maquina::Application::SessionsHeader.new(brand_icon: brand_icon) %>
4
+
5
+ <%= turbo_frame_tag :form do %>
6
+ <%= render Maquina::Sessions::Form.new(OpenStruct.new(params.slice(:email, :password, :return_to))) %>
7
+ <% end %>
8
+
9
+ </div>
@@ -0,0 +1 @@
1
+ 401
@@ -0,0 +1,40 @@
1
+ <h1 class="mt-0 text-2xl font-bold text-left text-gray-postmark-darker">
2
+ <%= t("mailers.invitation_email.hello") %>
3
+ </h1>
4
+ <p class="mt-1_5 mb-5px text-base leading-6 text-gray-postmark-dark">
5
+ <%= t("mailers.invitation_email.content_1", inviteer: @inviteer, org: @org, product: t("mailers.product_name")) %>
6
+ </p>
7
+ <table align="center" class="w-full text-center my-7_5 mx-auto" cellpadding="0" cellspacing="0" role="presentation">
8
+ <tr>
9
+ <td align="center">
10
+ <table class="w-full" cellpadding="0" cellspacing="0" role="presentation">
11
+ <tr>
12
+ <td align="center" class="text-base">
13
+ <a href="<%= @url %>" class="button button--blue"><%= t("mailers.invitation_email.action") %></a>
14
+ </td>
15
+ </tr>
16
+ </table>
17
+ </td>
18
+ </tr>
19
+ </table>
20
+ <p class="mt-1_5 mb-5px text-base leading-6 text-gray-postmark-dark">
21
+ <%= t("mailers.invitation_email.content_2").html_safe %>
22
+ </p>
23
+ <p class="mt-2_5 mb-5px text-base leading-6 text-gray-postmark-dark">
24
+ <%= t("mailers.invitation_email.content_3", org: @org).html_safe %>
25
+ </p>
26
+ <p class="mt-2_5 mb-5px text-base leading-6 text-gray-postmark-dark">
27
+ <%= t("mailers.invitation_email.content_4").html_safe %>
28
+ </p>
29
+ <table class="body-sub" cellpadding="0" cellspacing="0" role="presentation">
30
+ <tr>
31
+ <td>
32
+ <p class="mt-1_5 mb-5px text-xs leading-6 text-gray-postmark-dark">
33
+ <%= t("mailers.invitation_email.footer") %>
34
+ </p>
35
+ <p class="mt-1_5 mb-5px text-xs leading-6 text-gray-postmark-dark">
36
+ <%= @url %>
37
+ </p>
38
+ </td>
39
+ </tr>
40
+ </table>
@@ -0,0 +1,12 @@
1
+ <%= t("mailers.invitation_email.hello") %>
2
+
3
+ <%= t("mailers.invitation_email.content_1_text", inviteer: @inviteer, org: @org, product: t("mailers.product_name")) %>
4
+
5
+ <%= @url %>
6
+
7
+ <%= t("mailers.invitation_email.content_2_text") %>
8
+
9
+ <%= t("mailers.invitation_email.content_3_1_text") %>
10
+ <%= t("mailers.invitation_email.content_3_2_text", org: @org) %>
11
+
12
+ <%= t("mailers.invitation_email.content_4_text") %>
@@ -0,0 +1 @@
1
+ /Users/marioch/Development/butaca2/config/definitions.rb
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "maquina"
4
+
5
+ Maquina.configuration.importmap.draw do
6
+ # Stimulus & Turbo
7
+ pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
8
+ pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
9
+ pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
10
+ pin "stimulus-use", to: "https://ga.jspm.io/npm:stimulus-use@0.51.0/dist/index.js"
11
+ pin "hotkeys-js", to: "https://ga.jspm.io/npm:hotkeys-js@3.10.0/dist/hotkeys.esm.js"
12
+
13
+ # Maquina entrypoint
14
+ pin "application", to: "maquina/application.js", preload: true
15
+
16
+ pin_all_from Maquina::Engine.root.join("app/assets/javascripts/maquina/controllers"), under: "controllers", to: "maquina/controllers"
17
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "money-rails"
4
+
5
+ MoneyRails.configure do |config|
6
+ # To set the default currency
7
+ #
8
+ # config.default_currency = :usd
9
+
10
+ # Set default bank object
11
+ #
12
+ # Example:
13
+ # config.default_bank = EuCentralBank.new
14
+
15
+ # Add exchange rates to current money bank object.
16
+ # (The conversion rate refers to one direction only)
17
+ #
18
+ # Example:
19
+ # config.add_rate "USD", "CAD", 1.24515
20
+ # config.add_rate "CAD", "USD", 0.803115
21
+
22
+ # To handle the inclusion of validations for monetized fields
23
+ # The default value is true
24
+ #
25
+ # config.include_validations = true
26
+
27
+ # Default ActiveRecord migration configuration values for columns:
28
+ #
29
+ # config.amount_column = { prefix: '', # column name prefix
30
+ # postfix: '_cents', # column name postfix
31
+ # column_name: nil, # full column name (overrides prefix, postfix and accessor name)
32
+ # type: :integer, # column type
33
+ # present: true, # column will be created
34
+ # null: false, # other options will be treated as column options
35
+ # default: 0
36
+ # }
37
+ #
38
+ # config.currency_column = { prefix: '',
39
+ # postfix: '_currency',
40
+ # column_name: nil,
41
+ # type: :string,
42
+ # present: true,
43
+ # null: false,
44
+ # default: 'USD'
45
+ # }
46
+
47
+ # Register a custom currency
48
+ #
49
+ # Example:
50
+ # config.register_currency = {
51
+ # priority: 1,
52
+ # iso_code: "EU4",
53
+ # name: "Euro with subunit of 4 digits",
54
+ # symbol: "€",
55
+ # symbol_first: true,
56
+ # subunit: "Subcent",
57
+ # subunit_to_unit: 10000,
58
+ # thousands_separator: ".",
59
+ # decimal_mark: ","
60
+ # }
61
+
62
+ # Specify a rounding mode
63
+ # Any one of:
64
+ #
65
+ # BigDecimal::ROUND_UP,
66
+ # BigDecimal::ROUND_DOWN,
67
+ # BigDecimal::ROUND_HALF_UP,
68
+ # BigDecimal::ROUND_HALF_DOWN,
69
+ # BigDecimal::ROUND_HALF_EVEN,
70
+ # BigDecimal::ROUND_CEILING,
71
+ # BigDecimal::ROUND_FLOOR
72
+ #
73
+ # set to BigDecimal::ROUND_HALF_EVEN by default
74
+ #
75
+ config.rounding_mode = BigDecimal::ROUND_HALF_UP
76
+
77
+ # Set default money format globally.
78
+ # Default value is nil meaning "ignore this option".
79
+ # Example:
80
+ #
81
+ # config.default_format = {
82
+ # no_cents_if_whole: nil,
83
+ # symbol: nil,
84
+ # sign_before_symbol: nil
85
+ # }
86
+
87
+ # If you would like to use I18n localization (formatting depends on the
88
+ # locale):
89
+ config.locale_backend = :i18n
90
+ #
91
+ # Example (using default localization from rails-i18n):
92
+ #
93
+ # I18n.locale = :en
94
+ # Money.new(10_000_00, 'USD').format # => $10,000.00
95
+ # I18n.locale = :es
96
+ # Money.new(10_000_00, 'USD').format # => $10.000,00
97
+ #
98
+ # For the legacy behaviour of "per currency" localization (formatting depends
99
+ # only on currency):
100
+ # config.locale_backend = :currency
101
+ #
102
+ # Example:
103
+ # Money.new(10_000_00, 'USD').format # => $10,000.00
104
+ # Money.new(10_000_00, 'EUR').format # => €10.000,00
105
+ #
106
+ # In case you don't need localization and would like to use default values
107
+ # (can be redefined using config.default_format):
108
+ # config.locale_backend = nil
109
+
110
+ # Set default raise_error_on_money_parsing option
111
+ # It will be raise error if assigned different currency
112
+ # The default value is false
113
+ #
114
+ # Example:
115
+ # config.raise_error_on_money_parsing = false
116
+ end