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.
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