maquina 0.5.2 → 0.7.2
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 +4 -4
- data/.ruby-version +1 -1
- data/Gemfile +2 -0
- data/Gemfile.lock +3 -35
- data/app/assets/javascripts/controllers/alert_controller.js +22 -0
- data/app/assets/stylesheets/maquina/application.tailwind.css +4 -101
- data/app/assets/stylesheets/maquina.css +114 -0
- data/app/controllers/concerns/maquina/authenticate.rb +29 -1
- data/app/controllers/concerns/maquina/index.rb +81 -2
- data/app/controllers/concerns/maquina/resourceful.rb +95 -9
- data/app/controllers/maquina/application_controller.rb +1 -1
- data/app/controllers/maquina/dashboard_controller.rb +10 -5
- data/app/controllers/maquina/invitations_controller.rb +0 -2
- data/app/controllers/maquina/plans_controller.rb +13 -23
- data/app/controllers/maquina/sessions_controller.rb +1 -1
- data/app/controllers/maquina/users_controller.rb +0 -2
- data/app/helpers/maquina/application_helper.rb +0 -8
- data/app/helpers/maquina/navbar_menu_helper.rb +1 -1
- data/app/models/concerns/maquina/blockeable.rb +64 -0
- data/app/models/concerns/maquina/organization_scoped.rb +26 -0
- data/app/models/concerns/maquina/retain_passwords.rb +44 -0
- data/app/models/concerns/maquina/sqlite_search.rb +92 -0
- data/app/models/concerns/maquina/user_scoped.rb +26 -0
- data/app/models/maquina/active_session.rb +40 -0
- data/app/models/maquina/current.rb +39 -2
- data/app/models/maquina/invitation.rb +28 -0
- data/app/models/maquina/membership.rb +23 -1
- data/app/models/maquina/organization.rb +19 -2
- data/app/models/maquina/plan.rb +26 -6
- data/app/models/maquina/used_password.rb +30 -0
- data/app/models/maquina/user.rb +50 -8
- data/app/policies/maquina/application_policy.rb +1 -1
- data/app/policies/maquina/dashboard_policy.rb +7 -0
- data/app/policies/maquina/navigation_policy.rb +2 -0
- data/app/views/layouts/maquina/application.html.erb +2 -2
- data/app/views/layouts/maquina/sessions.html.erb +1 -1
- data/app/views/maquina/application/_navbar.html.erb +8 -3
- data/app/views/maquina/application/components/checkbox_component.rb +3 -3
- data/app/views/maquina/application/components/component_base.rb +3 -2
- data/app/views/maquina/application/edit.html.erb +10 -7
- data/app/views/maquina/application/filters.rb +118 -0
- data/app/views/maquina/application/index.html.erb +6 -3
- data/app/views/maquina/application/index_header.rb +5 -2
- data/app/views/maquina/application/index_table.rb +71 -6
- data/app/views/maquina/application/index_tools.rb +17 -0
- data/app/views/maquina/application/new.html.erb +10 -7
- data/app/views/maquina/application/search.rb +42 -0
- data/app/views/maquina/application/sessions_header.rb +3 -9
- data/app/views/maquina/dashboard/index.html.erb +4 -0
- data/app/views/maquina/dashboard/stats.rb +35 -0
- data/app/views/maquina/dashboard/tasks.rb +124 -0
- data/app/views/maquina/first_runs/form.rb +0 -2
- data/app/views/maquina/first_runs/show.html.erb +4 -1
- data/app/views/maquina/navbar/title.rb +4 -2
- data/config/importmap.rb +1 -13
- data/config/locales/flash.en.yml +6 -0
- data/config/locales/flash.es.yml +6 -0
- data/config/locales/forms.en.yml +33 -4
- data/config/locales/forms.es.yml +22 -11
- data/config/locales/models.en.yml +10 -0
- data/config/locales/models.es.yml +10 -0
- data/config/locales/views.en.yml +33 -5
- data/config/locales/views.es.yml +28 -10
- data/config/routes.rb +1 -0
- data/db/migrate/20221109010726_create_maquina_plans.rb +1 -1
- data/db/migrate/20221113000409_create_maquina_users.rb +1 -1
- data/db/migrate/20221113020108_create_maquina_used_passwords.rb +1 -1
- data/db/migrate/20221115223414_create_maquina_active_sessions.rb +1 -3
- data/db/migrate/20230201203922_create_maquina_invitations.rb +1 -1
- data/db/migrate/20230829183530_create_maquina_organizations.rb +1 -1
- data/db/migrate/20230829192656_create_maquina_memberships.rb +2 -4
- data/db/migrate/20241109191405_add_counter_cache_to_plans.rb +5 -0
- data/lib/generators/maquina/install_generator.rb +67 -1
- data/lib/generators/maquina/tailwind_config/templates/lib/generators/tailwind_config/templates/config/tailwind.config.js.tt +9 -5
- data/lib/generators/maquina/tailwind_config/templates/lib/tasks/tailwind.rake.tt +2 -0
- data/lib/generators/maquina/templates/config/initializers/maquina.rb.tt +2 -1
- data/lib/maquina/engine.rb +6 -3
- data/lib/maquina/version.rb +1 -1
- data/lib/maquina.rb +2 -9
- metadata +20 -76
- data/app/assets/javascripts/maquina/application.js +0 -4
- data/app/assets/javascripts/maquina/controllers/alert_controller.js +0 -29
- data/app/assets/javascripts/maquina/controllers/application.js +0 -9
- data/app/assets/javascripts/maquina/controllers/index.js +0 -11
- data/app/models/concerns/maquina/authenticate_by.rb +0 -33
- data/app/views/maquina/navbar/search.rb +0 -40
- data/lib/generators/maquina/install_templates/install_templates_generator.rb +0 -31
- /data/app/assets/javascripts/{maquina/controllers → controllers}/backdrop_controller.js +0 -0
- /data/app/assets/javascripts/{maquina/controllers → controllers}/file_controller.js +0 -0
- /data/app/assets/javascripts/{maquina/controllers → controllers}/mobile_menu_controller.js +0 -0
- /data/app/assets/javascripts/{maquina/controllers → controllers}/modal_controller.js +0 -0
- /data/app/assets/javascripts/{maquina/controllers → controllers}/modal_open_controller.js +0 -0
- /data/app/assets/javascripts/{maquina/controllers → controllers}/popup_menu_controller.js +0 -0
- /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
|
-
|
6
|
-
|
7
|
-
|
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") {
|
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,
|
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-
|
43
|
-
first_css = "py-
|
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)
|
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
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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.
|
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
|
-
|
21
|
-
|
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,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(
|
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
|
-
|
16
|
-
|
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
|
-
|
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
|
data/config/locales/flash.en.yml
CHANGED
data/config/locales/flash.es.yml
CHANGED
@@ -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:
|
data/config/locales/forms.en.yml
CHANGED
@@ -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:
|
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
|
data/config/locales/forms.es.yml
CHANGED
@@ -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:
|
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@
|
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
|