maquina 0.5.2 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +3 -35
  5. data/app/assets/javascripts/controllers/alert_controller.js +22 -0
  6. data/app/assets/stylesheets/maquina/application.tailwind.css +4 -101
  7. data/app/assets/stylesheets/maquina.css +114 -0
  8. data/app/controllers/concerns/maquina/authenticate.rb +29 -1
  9. data/app/controllers/concerns/maquina/index.rb +81 -2
  10. data/app/controllers/concerns/maquina/resourceful.rb +95 -9
  11. data/app/controllers/maquina/application_controller.rb +1 -1
  12. data/app/controllers/maquina/dashboard_controller.rb +10 -5
  13. data/app/controllers/maquina/invitations_controller.rb +0 -2
  14. data/app/controllers/maquina/plans_controller.rb +13 -23
  15. data/app/controllers/maquina/sessions_controller.rb +1 -1
  16. data/app/controllers/maquina/users_controller.rb +0 -2
  17. data/app/helpers/maquina/application_helper.rb +0 -8
  18. data/app/helpers/maquina/navbar_menu_helper.rb +1 -1
  19. data/app/models/concerns/maquina/blockeable.rb +64 -0
  20. data/app/models/concerns/maquina/organization_scoped.rb +26 -0
  21. data/app/models/concerns/maquina/retain_passwords.rb +44 -0
  22. data/app/models/concerns/maquina/sqlite_search.rb +92 -0
  23. data/app/models/concerns/maquina/user_scoped.rb +26 -0
  24. data/app/models/maquina/active_session.rb +40 -0
  25. data/app/models/maquina/current.rb +39 -2
  26. data/app/models/maquina/invitation.rb +28 -0
  27. data/app/models/maquina/membership.rb +23 -1
  28. data/app/models/maquina/organization.rb +19 -2
  29. data/app/models/maquina/plan.rb +26 -6
  30. data/app/models/maquina/used_password.rb +30 -0
  31. data/app/models/maquina/user.rb +50 -8
  32. data/app/policies/maquina/application_policy.rb +1 -1
  33. data/app/policies/maquina/dashboard_policy.rb +7 -0
  34. data/app/views/layouts/maquina/application.html.erb +2 -2
  35. data/app/views/layouts/maquina/sessions.html.erb +1 -1
  36. data/app/views/maquina/application/_navbar.html.erb +8 -3
  37. data/app/views/maquina/application/components/checkbox_component.rb +3 -3
  38. data/app/views/maquina/application/components/component_base.rb +3 -2
  39. data/app/views/maquina/application/edit.html.erb +10 -7
  40. data/app/views/maquina/application/filters.rb +118 -0
  41. data/app/views/maquina/application/index.html.erb +6 -3
  42. data/app/views/maquina/application/index_header.rb +5 -2
  43. data/app/views/maquina/application/index_table.rb +71 -6
  44. data/app/views/maquina/application/index_tools.rb +17 -0
  45. data/app/views/maquina/application/new.html.erb +10 -7
  46. data/app/views/maquina/application/search.rb +42 -0
  47. data/app/views/maquina/application/sessions_header.rb +3 -9
  48. data/app/views/maquina/dashboard/index.html.erb +4 -0
  49. data/app/views/maquina/dashboard/stats.rb +35 -0
  50. data/app/views/maquina/dashboard/tasks.rb +124 -0
  51. data/app/views/maquina/first_runs/form.rb +0 -2
  52. data/app/views/maquina/first_runs/show.html.erb +4 -1
  53. data/app/views/maquina/navbar/title.rb +4 -2
  54. data/config/importmap.rb +1 -13
  55. data/config/locales/flash.en.yml +6 -0
  56. data/config/locales/flash.es.yml +6 -0
  57. data/config/locales/forms.en.yml +33 -4
  58. data/config/locales/forms.es.yml +22 -11
  59. data/config/locales/models.en.yml +10 -0
  60. data/config/locales/models.es.yml +10 -0
  61. data/config/locales/views.en.yml +33 -5
  62. data/config/locales/views.es.yml +28 -10
  63. data/config/routes.rb +1 -0
  64. data/db/migrate/20221109010726_create_maquina_plans.rb +1 -1
  65. data/db/migrate/20221113000409_create_maquina_users.rb +1 -1
  66. data/db/migrate/20221113020108_create_maquina_used_passwords.rb +1 -1
  67. data/db/migrate/20221115223414_create_maquina_active_sessions.rb +1 -3
  68. data/db/migrate/20230201203922_create_maquina_invitations.rb +1 -1
  69. data/db/migrate/20230829183530_create_maquina_organizations.rb +1 -1
  70. data/db/migrate/20230829192656_create_maquina_memberships.rb +2 -4
  71. data/db/migrate/20241109191405_add_counter_cache_to_plans.rb +5 -0
  72. data/lib/generators/maquina/install_generator.rb +67 -1
  73. data/lib/generators/maquina/tailwind_config/templates/lib/generators/tailwind_config/templates/config/tailwind.config.js.tt +9 -5
  74. data/lib/generators/maquina/tailwind_config/templates/lib/tasks/tailwind.rake.tt +2 -0
  75. data/lib/generators/maquina/templates/config/initializers/maquina.rb.tt +2 -1
  76. data/lib/maquina/engine.rb +6 -3
  77. data/lib/maquina/version.rb +1 -1
  78. data/lib/maquina.rb +2 -9
  79. metadata +20 -76
  80. data/app/assets/javascripts/maquina/application.js +0 -4
  81. data/app/assets/javascripts/maquina/controllers/alert_controller.js +0 -29
  82. data/app/assets/javascripts/maquina/controllers/application.js +0 -9
  83. data/app/assets/javascripts/maquina/controllers/index.js +0 -11
  84. data/app/models/concerns/maquina/authenticate_by.rb +0 -33
  85. data/app/views/maquina/navbar/search.rb +0 -40
  86. data/lib/generators/maquina/install_templates/install_templates_generator.rb +0 -31
  87. /data/app/assets/javascripts/{maquina/controllers → controllers}/backdrop_controller.js +0 -0
  88. /data/app/assets/javascripts/{maquina/controllers → controllers}/file_controller.js +0 -0
  89. /data/app/assets/javascripts/{maquina/controllers → controllers}/mobile_menu_controller.js +0 -0
  90. /data/app/assets/javascripts/{maquina/controllers → controllers}/modal_controller.js +0 -0
  91. /data/app/assets/javascripts/{maquina/controllers → controllers}/modal_open_controller.js +0 -0
  92. /data/app/assets/javascripts/{maquina/controllers → controllers}/popup_menu_controller.js +0 -0
  93. /data/app/assets/javascripts/{maquina/controllers → controllers}/submit_form_controller.js +0 -0
@@ -1,10 +1,13 @@
1
1
  <div class="px-4 sm:px-6 lg:px-8">
2
2
  <%= render Maquina::Application::IndexHeader.new(params[:q]) %>
3
3
 
4
+ <%= render Maquina::Application::IndexTools.new %>
5
+
4
6
  <%= render Maquina::Application::IndexTable.new(
5
- collection: collection,
6
- pagination: @pagination,
7
- list_attributes: list_attributes) %>
7
+ collection: collection,
8
+ pagination: @pagination,
9
+ list_attributes: list_attributes,
10
+ ) %>
8
11
  </div>
9
12
 
10
13
  <%= render Maquina::Application::IndexModal.new %>
@@ -13,9 +13,8 @@ module Maquina
13
13
  def view_template
14
14
  div(class: "sm:flex sm:items-center") do
15
15
  div(class: "sm:flex-auto") do
16
- h1(class: "text-xl font-semibold text-skin-base") { model_human_name(plural: true) }
16
+ h1(class: "text-xl font-semibold text-skin-base") { model_title }
17
17
  p(class: "mt-2 text-sm text-skin-muted") { description }
18
- p(class: "mt-4 text-skin-base") { unsafe_raw t("index.search", search: @filter) } if @filter.present?
19
18
  end
20
19
  add_new_template
21
20
  end
@@ -23,6 +22,10 @@ module Maquina
23
22
 
24
23
  private
25
24
 
25
+ def model_title
26
+ t("index.#{resource_class.model_name.i18n_key}.title", model: model_human_name(plural: true).downcase, default: model_human_name(plural: true))
27
+ end
28
+
26
29
  def add_new_template
27
30
  return if policy_class.blank? || !allowed_to?(:new?, with: policy_class)
28
31
 
@@ -4,8 +4,10 @@ module Maquina
4
4
  module Application
5
5
  class IndexTable < Phlex::HTML
6
6
  include ApplicationView
7
+ include Phlex::Rails::Helpers::LinkTo
7
8
 
8
- delegate :humanized_money_with_symbol, :edit_resource_path, :resource_path, to: :helpers
9
+ delegate :humanized_money_with_symbol, :edit_resource_path, :resource_path, :collection_path, :params, :sortable,
10
+ to: :helpers
9
11
 
10
12
  def initialize(collection: nil, pagination: nil, list_attributes: nil)
11
13
  @collection = collection
@@ -39,8 +41,8 @@ module Maquina
39
41
  end
40
42
 
41
43
  def table_headers_template
42
- base_css = "px-3 py-3.5 text-left text-sm font-semibold text-skin-base"
43
- first_css = "py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-skin-base sm:pl-6"
44
+ base_css = "px-1 py-3 text-left text-sm font-medium text-skin-base"
45
+ first_css = "py-1 py-3 text-left text-sm font-medium text-skin-base sm:pl-6"
44
46
 
45
47
  @list_attributes.each_with_index do |attribute, index|
46
48
  current_css = if index == 0
@@ -49,7 +51,21 @@ module Maquina
49
51
  base_css
50
52
  end
51
53
 
52
- th(scope: :col, class: current_css) { attribute_human_name(attribute) }
54
+ th(scope: :col, class: current_css) do
55
+ if sortable.include?(attribute)
56
+ link_to(collection_path(sort_link_params(attribute)), class: "inline-flex items-center justify-center gap-2 whitespace-nowrap hover:bg-gray-100 hover:cursor-pointer p-2 rounded-sm") do
57
+ span { attribute_human_name(attribute) }
58
+ unsafe_raw case sort_status(attribute)
59
+ when :asc then asc_sort_icon
60
+ when :desc then desc_sort_icon
61
+ else
62
+ no_sort_icon
63
+ end
64
+ end
65
+ else
66
+ span { attribute_human_name(attribute) }
67
+ end
68
+ end
53
69
  end
54
70
  end
55
71
 
@@ -58,7 +74,7 @@ module Maquina
58
74
 
59
75
  tr do
60
76
  th(scope: "col", colspan: col_span, class: "whitespace-nowrap px-3 py-4 text-sm text-skin-dimmed") do
61
- t("index.no_items", model: model_human_name(plural: true))
77
+ t("index.no_items", model: model_human_name(plural: true).downcase)
62
78
  end
63
79
  end
64
80
  end
@@ -67,7 +83,7 @@ module Maquina
67
83
  base_css = "whitespace-nowrap px-3 py-4 text-sm text-skin-muted"
68
84
  first_css = "whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-skin-base sm:pl-6"
69
85
 
70
- tr do
86
+ tr(class: "hover:bg-gray-50") do
71
87
  @list_attributes.each_with_index do |attribute, index|
72
88
  td(class: (index == 0) ? first_css : base_css, scope: "col") { attribute_to_link(item, attribute, attribute_value(item, attribute)) }
73
89
  end
@@ -122,6 +138,55 @@ module Maquina
122
138
 
123
139
  link_to(value, resource_path(item), class: "link", data: {"turbo-frame": "_top"})
124
140
  end
141
+
142
+ def sort_link_params(attribute)
143
+ current_params = params.dup.to_unsafe_h.except(:controller, :action)
144
+ current_order = current_params[:order]
145
+ current_dir = current_params[:dir]
146
+
147
+ if current_order != attribute.to_s
148
+ current_params[:order] = attribute
149
+ current_params[:dir] = :desc
150
+ elsif current_dir == "desc"
151
+ current_params[:dir] = :asc
152
+ elsif current_dir == "asc"
153
+ current_params.except!(:order, :dir)
154
+ else
155
+ current_params[:order] = attribute
156
+ current_params[:dir] = :desc
157
+ end
158
+
159
+ current_params
160
+ end
161
+
162
+ def sort_status(attribute)
163
+ return nil unless params[:order] == attribute.to_s
164
+ (params[:dir] == "asc") ? :asc : :desc
165
+ end
166
+
167
+ def no_sort_icon
168
+ <<~SVG
169
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-auto text-skin-dimmed">
170
+ <path d="m7 15 5 5 5-5"></path><path d="m7 9 5-5 5 5"></path>
171
+ </svg>
172
+ SVG
173
+ end
174
+
175
+ def desc_sort_icon
176
+ <<~SVG
177
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-auto text-skin-dimmed">
178
+ <path d="M12 5v14"></path><path d="m19 12-7 7-7-7"></path>
179
+ </svg>
180
+ SVG
181
+ end
182
+
183
+ def asc_sort_icon
184
+ <<~SVG
185
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-auto text-skin-dimmed">
186
+ <path d="m5 12 7-7 7 7"></path><path d="M12 19V5"></path>
187
+ </svg>
188
+ SVG
189
+ end
125
190
  end
126
191
  end
127
192
  end
@@ -0,0 +1,17 @@
1
+ module Maquina
2
+ module Application
3
+ class IndexTools < Phlex::HTML
4
+ include ApplicationView
5
+ delegate :resource_class, :filters, to: :helpers
6
+
7
+ def view_template
8
+ div(class: "flex items-center justify-between mt-8") do
9
+ div(class: "flex flex-1 items-center space-x-2") do
10
+ render Maquina::Application::Search.new if resource_class.searchable?
11
+ render Maquina::Application::Filters.new if filters.any?
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,9 +1,12 @@
1
- <div class="bg-white">
2
- <div class="mx-auto max-w-7xl py-12 px-4 sm:px-6 lg:px-8">
3
- <div class="mx-auto max-w-3xl">
4
- <%= turbo_frame_tag class_to_form_frame(resource_class) do %>
5
- <%= render Maquina::Application::New.new(resource: resource) %>
6
- <% end %>
7
- </div>
1
+ <div
2
+ class="
3
+ bg-white mx-auto max-w-4xl py-12 px-4 sm:px-6 lg:px-8 border border-gray-200
4
+ rounded-lg shadow-sm
5
+ "
6
+ >
7
+ <div class="mx-auto max-w-3xl">
8
+ <%= turbo_frame_tag class_to_form_frame(resource_class) do %>
9
+ <%= render Maquina::Application::New.new(resource: resource) %>
10
+ <% end %>
8
11
  </div>
9
12
  </div>
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maquina
4
+ module Application
5
+ class Search < Phlex::HTML
6
+ include ApplicationView
7
+ include Phlex::Rails::Helpers::FormWith
8
+
9
+ delegate :t, :request, :collection_path, :params, to: :helpers
10
+
11
+ def view_template
12
+ current_params = request.params.except(:q, :page, :controller, :action)
13
+ current_params.reject! { |key, value| value.blank? }
14
+ query = params[:q]
15
+
16
+ form_with(url: @url, method: :get, data: {controller: "submit-form"}, class: "pr-3") do |form|
17
+ current_params.each do |key, value|
18
+ form.hidden_field key, value: value
19
+ end
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
+ unsafe_raw 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
+
31
+ private
32
+
33
+ def search_icon
34
+ <<~SVG
35
+ <svg class="h-5 w-5 text-skin-dimmed" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
36
+ <path d="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"></path>
37
+ </svg>
38
+ SVG
39
+ end
40
+ end
41
+ end
42
+ end
@@ -7,7 +7,7 @@ module Maquina
7
7
  include Phlex::DeferredRender
8
8
  include Phlex::Rails::Helpers::Translate
9
9
 
10
- def initialize(brand_icon:, translation_key: "maquina.application.sessions_header")
10
+ def initialize(brand_icon:, translation_key: "maquina.sessions.new")
11
11
  @brand_icon = brand_icon
12
12
  @translation_key = translation_key
13
13
  end
@@ -17,17 +17,11 @@ module Maquina
17
17
  image_tag(@brand_icon, class: "mx-auto h-12 w-auto", alt: t("application_name"))
18
18
  h2(class: "mt-6 text-center text-3xl font-bold tracking-tight text-skin-base") { t("#{@translation_key}.title") }
19
19
 
20
- if @description.present?
21
- p(class: "mt-2 text-center text-sm text-skin-dimmed") do
22
- yield_content(&@description)
23
- end
20
+ p(class: "mt-2 text-center text-sm text-skin-dimmed") do
21
+ t("#{@translation_key}.subtitle", default: "")
24
22
  end
25
23
  end
26
24
  end
27
-
28
- def description(&block)
29
- @description = block
30
- end
31
25
  end
32
26
  end
33
27
  end
@@ -0,0 +1,4 @@
1
+ <% if allowed_to? :management_stats?, with: policy_class %>
2
+ <%= render Maquina::Dashboard::Stats.new(stats: @stats) %>
3
+ <%= render Maquina::Dashboard::Tasks.new %>
4
+ <% end %>
@@ -0,0 +1,35 @@
1
+ module Maquina
2
+ module Dashboard
3
+ class Stats < Phlex::HTML
4
+ include Phlex::Rails::Helpers::Translate
5
+
6
+ def initialize(stats:)
7
+ @stats = stats
8
+ end
9
+
10
+ def view_template
11
+ div(class: "mx-auto max-w-lg") do
12
+ h3(class: "text-base font-semibold text-skin-base") { t("maquina.dashboard.index.stats.title") }
13
+
14
+ dl(class: "mt-5 grid grid-cols-2 gap-5 sm:grid-cols-#{@stats.length}") do
15
+ @stats.each do |stat|
16
+ stats_card(
17
+ label: stat[:label],
18
+ value: stat[:value]
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def stats_card(label:, value:)
28
+ div(class: "overflow-hidden rounded-lg bg-white px-4 py-5 shadow sm:p-6") do
29
+ dt(class: "truncate text-sm font-medium text-skin-dimmed") { label }
30
+ dd(class: "mt-1 text-3xl font-semibold tracking-tight text-skin-base") { value }
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,124 @@
1
+ module Maquina
2
+ module Dashboard
3
+ class Tasks < Phlex::HTML
4
+ include Maquina::ApplicationView
5
+
6
+ def initialize(translation_key: "maquina.dashboard.index.tasks")
7
+ @translation_key = translation_key
8
+ end
9
+
10
+ def view_template
11
+ div(class: "mx-auto max-w-lg mt-12") do
12
+ h2(class: "text-base font-semibold text-skin-base") do
13
+ t("#{@translation_key}.title")
14
+ end
15
+
16
+ p(class: "mt-1 text-sm text-skin-dimmed") do
17
+ t("#{@translation_key}.subtitle")
18
+ end
19
+
20
+ ul(
21
+ role: "list",
22
+ class: "mt-6 divide-y divide-gray-200 border-b border-t border-gray-200"
23
+ ) do
24
+ # Create Plan Task
25
+ task_item(
26
+ title: t("#{@translation_key}.create_plan.title"),
27
+ description: t("#{@translation_key}.create_plan.description"),
28
+ color: "bg-skin-accented",
29
+ path: plans_path,
30
+ icon: create_plan_icon
31
+ )
32
+
33
+ # Manage Users Task
34
+ task_item(
35
+ title: t("#{@translation_key}.manage_users.title"),
36
+ description: t("#{@translation_key}.manage_users.description"),
37
+ color: "bg-skin-alternate",
38
+ path: users_path,
39
+ icon: manage_users_icon
40
+ )
41
+
42
+ # Organization Settings Task
43
+ task_item(
44
+ title: t("#{@translation_key}.organization_settings.title"),
45
+ description: t("#{@translation_key}.organization_settings.description"),
46
+ color: "bg-skin-alternate-2",
47
+ path: "#",
48
+ icon: organization_settings_icon
49
+ )
50
+ end
51
+
52
+ # TODO: Uncomment or remove this code later
53
+ # div(class: "mt-6 flex") do
54
+ # a(href: "#", class: "text-sm font-medium text-indigo-600 hover:text-indigo-500") do
55
+ # t("#{@translation_key}.view_all")
56
+ # span(aria_hidden: "true") { " →" }
57
+ # end
58
+ # end
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def task_item(title:, description:, color:, path:, icon:)
65
+ li do
66
+ div(class: "group relative flex items-start space-x-3 py-4 hover:bg-white/75") do
67
+ div(class: "shrink-0") do
68
+ span(class: "inline-flex h-10 w-10 items-center justify-center rounded-lg #{color}") do
69
+ unsafe_raw icon
70
+ end
71
+ end
72
+
73
+ div(class: "min-w-0 flex-1") do
74
+ div(class: "text-sm font-medium text-skin-base") do
75
+ a(href: path) do
76
+ span(class: "absolute inset-0", aria_hidden: "true")
77
+ span { title }
78
+ end
79
+ end
80
+ p(class: "text-sm text-skin-dimmed") { description }
81
+ end
82
+
83
+ div(class: "shrink-0 self-center") do
84
+ unsafe_raw chevron_icon
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ def create_plan_icon
91
+ <<~SVG
92
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6 text-white">
93
+ <path stroke-linecap="round" stroke-linejoin="round" d="M13.5 16.875h3.375m0 0h3.375m-3.375 0V13.5m0 3.375v3.375M6 10.5h2.25a2.25 2.25 0 0 0 2.25-2.25V6a2.25 2.25 0 0 0-2.25-2.25H6A2.25 2.25 0 0 0 3.75 6v2.25A2.25 2.25 0 0 0 6 10.5Zm0 9.75h2.25A2.25 2.25 0 0 0 10.5 18v-2.25a2.25 2.25 0 0 0-2.25-2.25H6a2.25 2.25 0 0 0-2.25 2.25V18A2.25 2.25 0 0 0 6 20.25Zm9.75-9.75H18a2.25 2.25 0 0 0 2.25-2.25V6A2.25 2.25 0 0 0 18 3.75h-2.25A2.25 2.25 0 0 0 13.5 6v2.25a2.25 2.25 0 0 0 2.25 2.25Z" />
94
+ </svg>
95
+ SVG
96
+ end
97
+
98
+ def manage_users_icon
99
+ <<~SVG
100
+ <svg class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
101
+ <path stroke-linecap="round" stroke-linejoin="round" d="M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z" />
102
+ </svg>
103
+ SVG
104
+ end
105
+
106
+ def organization_settings_icon
107
+ <<~SVG
108
+ <svg class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
109
+ <path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
110
+ <path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
111
+ </svg>
112
+ SVG
113
+ end
114
+
115
+ def chevron_icon
116
+ <<~SVG
117
+ <svg class="h-5 w-5 text-gray-400 group-hover:text-skin-dimmed" viewBox="0 0 20 20" fill="currentColor">
118
+ <path fill-rule="evenodd" d="M8.22 5.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L11.94 10 8.22 6.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" />
119
+ </svg>
120
+ SVG
121
+ end
122
+ end
123
+ end
124
+ end
@@ -6,13 +6,11 @@ module Maquina
6
6
 
7
7
  def initialize(resource)
8
8
  @resource = resource
9
- @scope = "first_runs"
10
9
  end
11
10
 
12
11
  def view_template
13
12
  div(class: "mt-8 sm:mx-auto sm:w-full sm:max-w-md") do
14
13
  div(class: "bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10") do
15
- p(class: "text-center text-skin-muted") { t("maquina.application.first_runs_header.description") }
16
14
  form_with(model: @resource, url: first_run_path, method: :post, class: "space-y-6") do |form|
17
15
  text_field(form, field_name: :email, required: true)
18
16
  password_field(form, field_name: :password, required: true)
@@ -1,5 +1,8 @@
1
1
  <div class="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
2
2
 
3
- <%= render Maquina::Application::SessionsHeader.new(brand_icon: brand_icon, translation_key: "maquina.application.first_runs_header") %>
3
+ <%= render Maquina::Application::SessionsHeader.new(
4
+ brand_icon: brand_icon,
5
+ translation_key: "maquina.first_runs.show",
6
+ ) %>
4
7
  <%= render Maquina::FirstRuns::Form.new(@user) %>
5
8
  </div>
@@ -12,8 +12,10 @@ module Maquina
12
12
  def view_template
13
13
  div class: "flex-shrink-0 flex items-center" do
14
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")
15
+ a(href: root_path) do
16
+ img(src: @brand_icon, class: "hidden lg:block mx-auto h-8 w-auto")
17
+ img(src: @brand_icon, class: "block lg:hidden mx-auto h-8 w-auto")
18
+ end
17
19
  end
18
20
  end
19
21
  end
data/config/importmap.rb CHANGED
@@ -1,13 +1 @@
1
- # frozen_string_literal: true
2
-
3
- pin "application" # , to: "maquina/application.js", preload: true
4
-
5
- # Stimulus & Turbo
6
- pin "@hotwired/turbo-rails", to: "turbo.min.js"
7
- pin "@hotwired/stimulus", to: "@hotwired--stimulus.js" # @3.2.2
8
- pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
9
- pin "stimulus-use" # , to: "https://ga.jspm.io/npm:stimulus-use@0.52.2/dist/index.js"
10
-
11
- # Maquina entrypoint
12
-
13
- pin_all_from Maquina::Engine.root.join("app/assets/javascripts/maquina/controllers"), under: "controllers" # , to: "maquina/controllers"
1
+ pin_all_from File.expand_path("../app/assets/javascripts/controllers", __dir__), under: "controllers", preload: false
@@ -30,6 +30,12 @@ en:
30
30
  title: Invitation sent
31
31
  description: Invitation for %{email} expires in 3 days
32
32
 
33
+ first_runs:
34
+ create:
35
+ notice:
36
+ title: Admin account created
37
+ description: Welcome to maquina!
38
+
33
39
  sessions:
34
40
  create:
35
41
  notice:
@@ -30,6 +30,12 @@ es:
30
30
  title: Invitación enviada
31
31
  description: La inivitación para %{email} expira en 3 días
32
32
 
33
+ first_runs:
34
+ create:
35
+ notice:
36
+ title: Cuenta de administrador creada
37
+ description: Bienvenido a maquina!
38
+
33
39
  sessions:
34
40
  create:
35
41
  notice:
@@ -1,11 +1,21 @@
1
1
  en:
2
2
  new:
3
+ title: New %{model}
4
+ description: Complete the form to create a new %{model}
3
5
  maquina/plan:
4
- title: Configure a plan for your customers
6
+ title: Create subscription plan
7
+ description: Set up a new subscription plan with pricing and features
5
8
  maquina/invitation:
6
9
  title: Invite user to collaborate
7
10
  description: User will receive an invitation to join this application in the provided email.
8
11
 
12
+ edit:
13
+ title: Edit %{model}
14
+ description: Modify the %{model} details
15
+ maquina/plan:
16
+ title: Edit subscription plan
17
+ description: Modify existing subscription plan details and configurations
18
+
9
19
  form:
10
20
  sessions:
11
21
  email: Email address
@@ -21,12 +31,12 @@ en:
21
31
  email: user@mail.com
22
32
  password: Current password
23
33
 
24
- maquina/plan:
25
- name: Basic plan
26
-
27
34
  maquina/invitation:
28
35
  email: Enter user's email
29
36
 
37
+ first_runs:
38
+ email: admin@maquina.app
39
+
30
40
  help:
31
41
  maquina/plan:
32
42
  name: Descriptive plan name
@@ -34,6 +44,8 @@ en:
34
44
  price: Price for not free plans
35
45
  free: Check if plan is free of cost
36
46
  active: Active plans are available for customers
47
+ first_runs:
48
+ passwords: Use a strong password. The password must contain at least 8 characters, including uppercase and lowercase letters, numbers and special characters.
37
49
 
38
50
  helpers:
39
51
  cancel: Cancel
@@ -45,6 +57,9 @@ en:
45
57
  maquina/invitation:
46
58
  create: Send invitation
47
59
 
60
+ first_runs:
61
+ create: Create admin account
62
+
48
63
  maxlength:
49
64
  default: 30
50
65
 
@@ -53,6 +68,20 @@ en:
53
68
  trial: 3
54
69
  price: 9
55
70
 
71
+ first_runs:
72
+ email: 60
73
+ password: 30
74
+
56
75
  sessions:
57
76
  email: 60
58
77
  password: 60
78
+
79
+ placeholder:
80
+
81
+ help:
82
+ maquina/plan:
83
+ name: Descriptive and unique plan name
84
+ trial: Number of days on trial
85
+ price: Price for not free plans
86
+ free: Check if plan is free of cost
87
+ active: Active plans are available for customers
@@ -1,11 +1,21 @@
1
1
  es:
2
2
  new:
3
+ title: Nuevo %{model}
4
+ description: Complete el formulario para crear un nuevo %{model}
3
5
  maquina/plan:
4
- title: Configura el plan para los clientes
6
+ title: Crear plan de suscripción
7
+ description: Configura un nuevo plan de suscripción con precios y características
5
8
  maquina/invitation:
6
9
  title: Invitar a un usuario a colaborar
7
10
  description: El usuario va a recibir la invitación para unirse a colaborar vía correo electrónico.
8
11
 
12
+ edit:
13
+ title: Editar %{model}
14
+ description: Modificar los detalles de %{model}
15
+ maquina/plan:
16
+ title: Editar plan de suscripción
17
+ description: Modifica los detalles y configuraciones del plan de suscripción
18
+
9
19
  form:
10
20
  sessions:
11
21
  email: Correo electrónico
@@ -21,22 +31,13 @@ es:
21
31
  email: user@mail.com
22
32
  password: Contraseña actual
23
33
 
24
- maquina/plan:
25
- name: Plan básico
26
-
27
34
  maquina/invitation:
28
35
  email: Correo electrónico del usuario
29
36
 
30
37
  first_runs:
31
- email: admin@aoorora.com
38
+ email: admin@maquina.app
32
39
 
33
40
  help:
34
- maquina/plan:
35
- name: Nombre del plan
36
- trial: Número de días de prueba
37
- price: Precio del plan
38
- free: Indica si el plan es gratuito
39
- active: Activar plan para clientes
40
41
  first_runs:
41
42
  password: Utilize una contraseña robusta. La contraseña debe contener al menos 8 caracteres, incluyendo letras mayúsculas y minúsculas, números y caracteres especiales.
42
43
 
@@ -68,3 +69,13 @@ es:
68
69
  sessions:
69
70
  email: 60
70
71
  password: 60
72
+
73
+ placeholder:
74
+
75
+ help:
76
+ maquina/plan:
77
+ name: Nombre del plan
78
+ trial: Número de días de prueba
79
+ price: Precio del plan
80
+ free: Indica si el plan es gratuito
81
+ active: Activar plan para clientes