lesli_admin 1.1.1 → 1.1.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/Rakefile +3 -0
- data/app/assets/stylesheets/lesli_admin/application.css +648 -584
- data/app/controllers/lesli_admin/account/details_controller.rb +11 -4
- data/app/controllers/lesli_admin/account/locations_controller.rb +8 -4
- data/app/controllers/lesli_admin/accounts_controller.rb +41 -27
- data/app/controllers/lesli_admin/dashboards_controller.rb +21 -0
- data/app/models/lesli_admin/account/currency.rb +4 -2
- data/app/models/lesli_admin/account/detail.rb +3 -0
- data/app/models/lesli_admin/account/location.rb +12 -2
- data/app/models/lesli_admin/account/setting.rb +4 -2
- data/app/models/lesli_admin/account.rb +18 -1
- data/app/models/lesli_admin/dashboard.rb +8 -1
- data/app/services/lesli_admin/account_service.rb +43 -16
- data/app/views/lesli_admin/accounts/show.html.erb +104 -7
- data/app/views/lesli_admin/dashboards/_component-account-metadata.html.erb +44 -0
- data/app/views/lesli_admin/dashboards/_component-installed-engines.html.erb +1 -1
- data/app/views/lesli_admin/dashboards/_component-roles.html.erb +1 -0
- data/app/views/lesli_admin/dashboards/_component-users.html.erb +1 -0
- data/app/views/lesli_admin/settings/_configuration_value.html.erb +52 -0
- data/app/views/lesli_admin/settings/_defaults.html.erb +145 -58
- data/db/migrate/v1/0101000210_create_lesli_admin_account_details.rb +7 -1
- data/db/migrate/v1/0101000410_create_lesli_admin_account_locations.rb +6 -1
- data/lib/lesli_admin/engine.rb +0 -4
- data/lib/lesli_admin/version.rb +1 -1
- data/readme.md +8 -5
- metadata +10 -5
- data/app/assets/config/lesli_admin_manifest.js +0 -38
- data/app/assets/stylesheets/lesli_admin/application.tailwind.css +0 -2
|
@@ -4,7 +4,7 @@ module LesliAdmin
|
|
|
4
4
|
|
|
5
5
|
# GET /account/details
|
|
6
6
|
def index
|
|
7
|
-
@account_details = Account::Detail.
|
|
7
|
+
@account_details = Account::Detail.where(account: admin_account)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
# GET /account/details/1
|
|
@@ -13,7 +13,7 @@ module LesliAdmin
|
|
|
13
13
|
|
|
14
14
|
# GET /account/details/new
|
|
15
15
|
def new
|
|
16
|
-
@account_detail =
|
|
16
|
+
@account_detail = admin_account.build_detail
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
# GET /account/details/1/edit
|
|
@@ -22,7 +22,7 @@ module LesliAdmin
|
|
|
22
22
|
|
|
23
23
|
# POST /account/details
|
|
24
24
|
def create
|
|
25
|
-
@account_detail =
|
|
25
|
+
@account_detail = admin_account.build_detail(account_detail_params)
|
|
26
26
|
|
|
27
27
|
if @account_detail.save
|
|
28
28
|
redirect_to @account_detail, notice: "Detail was successfully created.", allow_other_host: false
|
|
@@ -49,7 +49,14 @@ module LesliAdmin
|
|
|
49
49
|
private
|
|
50
50
|
# Use callbacks to share common setup or constraints between actions.
|
|
51
51
|
def set_account_detail
|
|
52
|
-
@account_detail = Account::Detail.
|
|
52
|
+
@account_detail = Account::Detail.find_by!(
|
|
53
|
+
id: params.expect(:id),
|
|
54
|
+
account: admin_account
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def admin_account
|
|
59
|
+
current_user.account.admin
|
|
53
60
|
end
|
|
54
61
|
|
|
55
62
|
# Only allow a list of trusted parameters through.
|
|
@@ -4,7 +4,7 @@ module LesliAdmin
|
|
|
4
4
|
|
|
5
5
|
# GET /account/locations
|
|
6
6
|
def index
|
|
7
|
-
@account_locations =
|
|
7
|
+
@account_locations = admin_account.locations
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
# GET /account/locations/1
|
|
@@ -13,7 +13,7 @@ module LesliAdmin
|
|
|
13
13
|
|
|
14
14
|
# GET /account/locations/new
|
|
15
15
|
def new
|
|
16
|
-
@account_location =
|
|
16
|
+
@account_location = admin_account.locations.build
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
# GET /account/locations/1/edit
|
|
@@ -22,7 +22,7 @@ module LesliAdmin
|
|
|
22
22
|
|
|
23
23
|
# POST /account/locations
|
|
24
24
|
def create
|
|
25
|
-
@account_location =
|
|
25
|
+
@account_location = admin_account.locations.build(account_location_params)
|
|
26
26
|
|
|
27
27
|
if @account_location.save
|
|
28
28
|
redirect_to @account_location, notice: "Location was successfully created.", allow_other_host: false
|
|
@@ -49,7 +49,11 @@ module LesliAdmin
|
|
|
49
49
|
private
|
|
50
50
|
# Use callbacks to share common setup or constraints between actions.
|
|
51
51
|
def set_account_location
|
|
52
|
-
@account_location =
|
|
52
|
+
@account_location = admin_account.locations.find(params.expect(:id))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def admin_account
|
|
56
|
+
current_user.account.admin
|
|
53
57
|
end
|
|
54
58
|
|
|
55
59
|
# Only allow a list of trusted parameters through.
|
|
@@ -36,25 +36,31 @@ module LesliAdmin
|
|
|
36
36
|
|
|
37
37
|
def show
|
|
38
38
|
@account = AccountService.new(current_user, query).show
|
|
39
|
+
|
|
39
40
|
respond_to do |format|
|
|
40
41
|
format.html
|
|
41
42
|
format.turbo_stream
|
|
42
43
|
format.json do
|
|
43
|
-
respond_with_successful(@account)
|
|
44
|
+
respond_with_successful(account_payload(@account))
|
|
44
45
|
end
|
|
45
46
|
end
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
def update
|
|
49
|
-
|
|
50
|
-
# check saved
|
|
51
|
-
if @account.update(account_params)
|
|
50
|
+
if AccountService.new(current_user, query).update(account_params)
|
|
52
51
|
log(:description => 'Updated the account details')
|
|
53
52
|
respond_with_lesli(
|
|
54
|
-
:
|
|
53
|
+
turbo: stream_notification_success("Account updated successfully"),
|
|
54
|
+
json: account_payload(@account),
|
|
55
|
+
redirect: account_path
|
|
56
|
+
)
|
|
57
|
+
else
|
|
58
|
+
respond_with_lesli(
|
|
59
|
+
turbo: stream_notification_danger(@account.errors.full_messages.to_sentence),
|
|
60
|
+
json: { errors: @account.errors.to_hash },
|
|
61
|
+
html: :show,
|
|
62
|
+
status: :unprocessable_content
|
|
55
63
|
)
|
|
56
|
-
else
|
|
57
|
-
#respond_with_error(@account.errors)
|
|
58
64
|
end
|
|
59
65
|
end
|
|
60
66
|
|
|
@@ -69,31 +75,39 @@ module LesliAdmin
|
|
|
69
75
|
# Only allow a list of trusted parameters through.
|
|
70
76
|
def account_params
|
|
71
77
|
params.require(:account).permit(
|
|
72
|
-
:id,
|
|
73
78
|
:name,
|
|
74
79
|
:email,
|
|
80
|
+
:status,
|
|
81
|
+
:region,
|
|
75
82
|
detail_attributes: [
|
|
76
|
-
:
|
|
77
|
-
:
|
|
83
|
+
:company_name,
|
|
84
|
+
:company_name_legal,
|
|
85
|
+
:company_tagline,
|
|
86
|
+
:country,
|
|
87
|
+
:address,
|
|
88
|
+
:region,
|
|
89
|
+
:city,
|
|
90
|
+
:postal_code,
|
|
91
|
+
:website,
|
|
92
|
+
:phone_number_1,
|
|
93
|
+
:phone_number_2,
|
|
94
|
+
:phone_number_3,
|
|
95
|
+
:phone_number_4,
|
|
96
|
+
:public_email,
|
|
97
|
+
:github,
|
|
98
|
+
:twitter,
|
|
99
|
+
:youtube,
|
|
100
|
+
:linkedin,
|
|
101
|
+
:facebook
|
|
78
102
|
]
|
|
79
|
-
# :company_name_legal,
|
|
80
|
-
# :company_tagline,
|
|
81
|
-
# :country,
|
|
82
|
-
# :city,
|
|
83
|
-
# :postal_code,
|
|
84
|
-
# :address,
|
|
85
|
-
# :region,
|
|
86
|
-
# :website,
|
|
87
|
-
# :phone_number_1,
|
|
88
|
-
# :public_email,
|
|
89
|
-
# :github,
|
|
90
|
-
# :twitter,
|
|
91
|
-
# :youtube,
|
|
92
|
-
# :linkedin,
|
|
93
|
-
# :facebook,
|
|
94
|
-
# :status,
|
|
95
|
-
# :users_id
|
|
96
103
|
)
|
|
97
104
|
end
|
|
105
|
+
|
|
106
|
+
def account_payload(account)
|
|
107
|
+
account.as_json.merge(
|
|
108
|
+
"detail_attributes" => (account.admin.detail&.as_json || {})
|
|
109
|
+
)
|
|
110
|
+
end
|
|
111
|
+
|
|
98
112
|
end
|
|
99
113
|
end
|
|
@@ -32,5 +32,26 @@ Building a better future, one line of code at a time.
|
|
|
32
32
|
|
|
33
33
|
module LesliAdmin
|
|
34
34
|
class DashboardsController < Lesli::Shared::DashboardsController
|
|
35
|
+
def show
|
|
36
|
+
@account_summary = AccountService.new(current_user, query).summary
|
|
37
|
+
|
|
38
|
+
synchronize_dashboard_components if defined?(LesliDashboard)
|
|
39
|
+
|
|
40
|
+
return super if defined?(LesliDashboard)
|
|
41
|
+
|
|
42
|
+
render :show
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def synchronize_dashboard_components
|
|
48
|
+
dashboard_account = current_user.account.dashboard
|
|
49
|
+
return unless dashboard_account
|
|
50
|
+
|
|
51
|
+
LesliDashboard::DashboardService.new(current_user).register(dashboard_account)
|
|
52
|
+
|
|
53
|
+
dashboard = dashboard_account.dashboards.find_by(engine: "LesliAdmin")
|
|
54
|
+
dashboard&.components&.where(component: Dashboard::RETIRED_COMPONENTS)&.destroy_all
|
|
55
|
+
end
|
|
35
56
|
end
|
|
36
57
|
end
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
module LesliAdmin
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
class Account::Currency < ApplicationRecord
|
|
3
|
+
belongs_to :account, class_name: "LesliAdmin::Account", inverse_of: :currencies
|
|
4
|
+
belongs_to :user, class_name: "Lesli::User", optional: true
|
|
5
|
+
end
|
|
4
6
|
end
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
module LesliAdmin
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
class Account::Location < ApplicationRecord
|
|
3
|
+
belongs_to :account, class_name: "LesliAdmin::Account", inverse_of: :locations
|
|
4
|
+
belongs_to :parent, class_name: "LesliAdmin::Account::Location", optional: true
|
|
5
|
+
|
|
6
|
+
has_many(
|
|
7
|
+
:children,
|
|
8
|
+
class_name: "LesliAdmin::Account::Location",
|
|
9
|
+
foreign_key: :parent_id,
|
|
10
|
+
inverse_of: :parent,
|
|
11
|
+
dependent: :nullify
|
|
12
|
+
)
|
|
13
|
+
end
|
|
4
14
|
end
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
module LesliAdmin
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
class Account::Setting < ApplicationRecord
|
|
3
|
+
belongs_to :account, class_name: "LesliAdmin::Account", inverse_of: :settings
|
|
4
|
+
belongs_to :user, class_name: "Lesli::User", optional: true
|
|
5
|
+
end
|
|
4
6
|
end
|
|
@@ -32,8 +32,25 @@ Building a better future, one line of code at a time.
|
|
|
32
32
|
|
|
33
33
|
module LesliAdmin
|
|
34
34
|
class Account < ApplicationRecord
|
|
35
|
-
belongs_to :account, class_name: "Lesli::Account"
|
|
35
|
+
belongs_to :account, class_name: "Lesli::Account", inverse_of: :admin
|
|
36
|
+
validates :account_id, uniqueness: true
|
|
36
37
|
has_many :dashboards
|
|
38
|
+
has_many :settings, class_name: "LesliAdmin::Account::Setting", dependent: :destroy
|
|
39
|
+
has_many :currencies, class_name: "LesliAdmin::Account::Currency", dependent: :destroy
|
|
40
|
+
has_many(
|
|
41
|
+
:locations,
|
|
42
|
+
class_name: "LesliAdmin::Account::Location",
|
|
43
|
+
inverse_of: :account,
|
|
44
|
+
dependent: :destroy
|
|
45
|
+
)
|
|
46
|
+
has_one(
|
|
47
|
+
:detail,
|
|
48
|
+
class_name: "LesliAdmin::Account::Detail",
|
|
49
|
+
inverse_of: :account,
|
|
50
|
+
dependent: :destroy
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
accepts_nested_attributes_for :detail, update_only: true
|
|
37
54
|
|
|
38
55
|
after_create :initialize_account
|
|
39
56
|
|
|
@@ -32,6 +32,13 @@ Building a better future, one line of code at a time.
|
|
|
32
32
|
|
|
33
33
|
module LesliAdmin
|
|
34
34
|
class Dashboard < Lesli::Shared::Dashboard
|
|
35
|
-
|
|
35
|
+
RETIRED_COMPONENTS = %w[locations currencies settings].freeze
|
|
36
|
+
|
|
37
|
+
COMPONENTS = [
|
|
38
|
+
{ users: { size: 4, position: 1 } },
|
|
39
|
+
{ roles: { size: 4, position: 2 } },
|
|
40
|
+
{ installed_engines: { size: 4, position: 3 } },
|
|
41
|
+
{ account_metadata: { size: 12, position: 4 } }
|
|
42
|
+
].freeze
|
|
36
43
|
end
|
|
37
44
|
end
|
|
@@ -32,25 +32,52 @@ Building a better future, one line of code at a time.
|
|
|
32
32
|
|
|
33
33
|
module LesliAdmin
|
|
34
34
|
class AccountService < Lesli::ApplicationLesliService
|
|
35
|
-
def show
|
|
36
|
-
account = Lesli::Account
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
def show
|
|
36
|
+
account = Lesli::Account
|
|
37
|
+
.includes(:user, admin: :detail)
|
|
38
|
+
.find(current_user.account.id)
|
|
39
|
+
|
|
40
|
+
account.admin.build_detail unless account.admin.detail
|
|
41
|
+
account
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def update(attributes)
|
|
45
|
+
account = current_user.account
|
|
46
|
+
account_attributes = attributes.to_h.deep_symbolize_keys
|
|
47
|
+
detail_attributes = account_attributes.delete(:detail_attributes)
|
|
48
|
+
|
|
49
|
+
ActiveRecord::Base.transaction do
|
|
50
|
+
account.update!(account_attributes)
|
|
51
|
+
account.admin.update!(detail_attributes: detail_attributes) if detail_attributes
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
account
|
|
55
|
+
rescue ActiveRecord::RecordInvalid => exception
|
|
56
|
+
copy_errors(exception.record, to: account)
|
|
57
|
+
false
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def summary
|
|
61
|
+
account = Lesli::Account
|
|
62
|
+
.includes(:user, :users, :roles)
|
|
63
|
+
.find(current_user.account.id)
|
|
40
64
|
|
|
41
65
|
{
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
# detail_attributes: {
|
|
47
|
-
# company_name_legal: account.company_name_legal,
|
|
48
|
-
# company_tagline: account.company_tagline
|
|
49
|
-
# }
|
|
66
|
+
account: account,
|
|
67
|
+
users: account.users.size,
|
|
68
|
+
roles: account.roles.size,
|
|
69
|
+
installed_engines: LesliSystem.engines.size
|
|
50
70
|
}
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def copy_errors(source, to:)
|
|
76
|
+
return if source.equal?(to)
|
|
51
77
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
78
|
+
source.errors.each do |error|
|
|
79
|
+
to.errors.add(:detail, error.full_message)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
55
82
|
end
|
|
56
83
|
end
|
|
@@ -1,15 +1,112 @@
|
|
|
1
|
-
|
|
2
1
|
<%= render(LesliView::Layout::Container.new("accounts")) do %>
|
|
2
|
+
<%= render(
|
|
3
|
+
LesliView::Components::Header.new(
|
|
4
|
+
"Account",
|
|
5
|
+
"Manage the core identity, company profile, and resources for #{@account.name}."
|
|
6
|
+
)
|
|
7
|
+
) %>
|
|
3
8
|
|
|
4
|
-
|
|
9
|
+
<% if @account.errors.any? %>
|
|
10
|
+
<div class="mb-6 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700" role="alert">
|
|
11
|
+
<p class="font-semibold">The account could not be updated.</p>
|
|
12
|
+
<ul class="mt-2 list-disc space-y-1 pl-5">
|
|
13
|
+
<% @account.errors.full_messages.each do |message| %>
|
|
14
|
+
<li><%= message %></li>
|
|
15
|
+
<% end %>
|
|
16
|
+
</ul>
|
|
17
|
+
</div>
|
|
18
|
+
<% end %>
|
|
5
19
|
|
|
6
20
|
<%= render LesliView::Components::Tabs.new do |tabs| %>
|
|
7
|
-
<% tabs.with_tab(title: "General
|
|
21
|
+
<% tabs.with_tab(id: "general", title: "General", icon: "business") do %>
|
|
8
22
|
<%= form_with(model: @account, url: account_path, builder: LesliView::Forms::Builder) do |form| %>
|
|
9
|
-
<%= form.fieldset do %>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
23
|
+
<%= form.fieldset("Core account") do %>
|
|
24
|
+
<p class="mb-5 text-sm leading-6 text-slate-500">
|
|
25
|
+
These values identify the account throughout every installed Lesli engine.
|
|
26
|
+
</p>
|
|
27
|
+
|
|
28
|
+
<div class="grid grid-cols-1 gap-x-5 md:grid-cols-2">
|
|
29
|
+
<%= form.field_control(
|
|
30
|
+
:name,
|
|
31
|
+
label: "Account name",
|
|
32
|
+
autocomplete: "organization",
|
|
33
|
+
required: true
|
|
34
|
+
) %>
|
|
35
|
+
<%= form.field_control_email(
|
|
36
|
+
:email,
|
|
37
|
+
label: "Account email",
|
|
38
|
+
autocomplete: "email",
|
|
39
|
+
required: true
|
|
40
|
+
) %>
|
|
41
|
+
<%= form.field_control_select(
|
|
42
|
+
:status,
|
|
43
|
+
Lesli::Account.statuses.keys.map { |status| [status, status] },
|
|
44
|
+
label: "Status"
|
|
45
|
+
) %>
|
|
46
|
+
<%= form.field_control_select(
|
|
47
|
+
:region,
|
|
48
|
+
Lesli::Account.regions.keys.map { |region| [region, region] },
|
|
49
|
+
label: "Data region"
|
|
50
|
+
) %>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<div class="mt-2 flex justify-end">
|
|
54
|
+
<%= form.field_control_button("Save account", category: "primary", icon: "save") %>
|
|
55
|
+
</div>
|
|
56
|
+
<% end %>
|
|
57
|
+
<% end %>
|
|
58
|
+
<% end %>
|
|
59
|
+
|
|
60
|
+
<% tabs.with_tab(id: "company-profile", title: "Company profile", icon: "domain") do %>
|
|
61
|
+
<%= form_with(model: @account, url: account_path, builder: LesliView::Forms::Builder) do |form| %>
|
|
62
|
+
<%= form.fields_for(:detail, @account.admin.detail) do |detail| %>
|
|
63
|
+
<%= detail.fieldset("Company information") do %>
|
|
64
|
+
<div class="grid grid-cols-1 gap-x-5 md:grid-cols-2">
|
|
65
|
+
<%= detail.field_control(:company_name, label: "Public company name", autocomplete: "organization") %>
|
|
66
|
+
<%= detail.field_control(:company_name_legal, label: "Legal company name") %>
|
|
67
|
+
<div class="md:col-span-2">
|
|
68
|
+
<%= detail.field_control(:company_tagline, label: "Tagline") %>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
<% end %>
|
|
72
|
+
|
|
73
|
+
<%= detail.fieldset("Address and contact") do %>
|
|
74
|
+
<div class="grid grid-cols-1 gap-x-5 md:grid-cols-2">
|
|
75
|
+
<div class="md:col-span-2">
|
|
76
|
+
<%= detail.field_control(:address, label: "Address", autocomplete: "street-address") %>
|
|
77
|
+
</div>
|
|
78
|
+
<%= detail.field_control(:city, label: "City", autocomplete: "address-level2") %>
|
|
79
|
+
<%= detail.field_control(:region, label: "State or region", autocomplete: "address-level1") %>
|
|
80
|
+
<%= detail.field_control(
|
|
81
|
+
:country,
|
|
82
|
+
label: "Country catalog ID",
|
|
83
|
+
message: "Numeric identifier from the configured country catalog.",
|
|
84
|
+
type: "number",
|
|
85
|
+
min: 0
|
|
86
|
+
) %>
|
|
87
|
+
<%= detail.field_control(:postal_code, label: "Postal code", autocomplete: "postal-code") %>
|
|
88
|
+
<%= detail.field_control_email(:public_email, label: "Public email", autocomplete: "email") %>
|
|
89
|
+
<%= detail.field_control(:website, label: "Website", type: "url", autocomplete: "url") %>
|
|
90
|
+
<%= detail.field_control(:phone_number_1, label: "Primary phone", type: "tel", autocomplete: "tel") %>
|
|
91
|
+
<%= detail.field_control(:phone_number_2, label: "Secondary phone", type: "tel") %>
|
|
92
|
+
<%= detail.field_control(:phone_number_3, label: "Additional phone 1", type: "tel") %>
|
|
93
|
+
<%= detail.field_control(:phone_number_4, label: "Additional phone 2", type: "tel") %>
|
|
94
|
+
</div>
|
|
95
|
+
<% end %>
|
|
96
|
+
|
|
97
|
+
<%= detail.fieldset("Social profiles") do %>
|
|
98
|
+
<div class="grid grid-cols-1 gap-x-5 md:grid-cols-2">
|
|
99
|
+
<%= detail.field_control(:github, label: "GitHub", type: "url") %>
|
|
100
|
+
<%= detail.field_control(:linkedin, label: "LinkedIn", type: "url") %>
|
|
101
|
+
<%= detail.field_control(:twitter, label: "X / Twitter", type: "url") %>
|
|
102
|
+
<%= detail.field_control(:facebook, label: "Facebook", type: "url") %>
|
|
103
|
+
<%= detail.field_control(:youtube, label: "YouTube", type: "url") %>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<div class="mt-2 flex justify-end">
|
|
107
|
+
<%= detail.field_control_button("Save company profile", category: "primary", icon: "save") %>
|
|
108
|
+
</div>
|
|
109
|
+
<% end %>
|
|
13
110
|
<% end %>
|
|
14
111
|
<% end %>
|
|
15
112
|
<% end %>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<%
|
|
2
|
+
account = @account_summary[:account]
|
|
3
|
+
status_classes = case account.status
|
|
4
|
+
when "active"
|
|
5
|
+
"bg-emerald-100 text-emerald-700"
|
|
6
|
+
when "suspended"
|
|
7
|
+
"bg-red-100 text-red-700"
|
|
8
|
+
when "onboarding"
|
|
9
|
+
"bg-amber-100 text-amber-700"
|
|
10
|
+
else
|
|
11
|
+
"bg-sky-100 text-sky-700"
|
|
12
|
+
end
|
|
13
|
+
%>
|
|
14
|
+
|
|
15
|
+
<section class="h-full rounded-2xl border border-slate-200 bg-white p-5 shadow-sm sm:p-6" aria-labelledby="account-metadata-title">
|
|
16
|
+
<div class="flex flex-wrap items-start justify-between gap-4">
|
|
17
|
+
<div>
|
|
18
|
+
<h2 id="account-metadata-title" class="text-base font-semibold text-slate-900">Account metadata</h2>
|
|
19
|
+
<p class="mt-1 text-sm text-slate-500">Read-only identifiers and lifecycle information.</p>
|
|
20
|
+
</div>
|
|
21
|
+
<span class="inline-flex items-center rounded-full px-2.5 py-1 text-xs font-semibold <%= status_classes %>">
|
|
22
|
+
<%= account.status.humanize %>
|
|
23
|
+
</span>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<dl class="mt-5 grid grid-cols-1 gap-x-6 gap-y-5 sm:grid-cols-2 xl:grid-cols-4">
|
|
27
|
+
<div>
|
|
28
|
+
<dt class="text-sm font-medium text-slate-500">Account ID</dt>
|
|
29
|
+
<dd class="mt-1 break-all text-sm text-slate-900"><%= account.id %></dd>
|
|
30
|
+
</div>
|
|
31
|
+
<div>
|
|
32
|
+
<dt class="text-sm font-medium text-slate-500">Owner</dt>
|
|
33
|
+
<dd class="mt-1 break-all text-sm text-slate-900"><%= account.user&.email || "Not assigned" %></dd>
|
|
34
|
+
</div>
|
|
35
|
+
<div>
|
|
36
|
+
<dt class="text-sm font-medium text-slate-500">Created</dt>
|
|
37
|
+
<dd class="mt-1 text-sm text-slate-900"><%= l(account.created_at, format: :long) %></dd>
|
|
38
|
+
</div>
|
|
39
|
+
<div>
|
|
40
|
+
<dt class="text-sm font-medium text-slate-500">Last updated</dt>
|
|
41
|
+
<dd class="mt-1 text-sm text-slate-900"><%= l(account.updated_at, format: :long) %></dd>
|
|
42
|
+
</div>
|
|
43
|
+
</dl>
|
|
44
|
+
</section>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<%= render(LesliView::Widgets::Count.new(
|
|
1
|
+
<%= render(LesliView::Widgets::Count.new("Installed engines", @account_summary[:installed_engines])) %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= render(LesliView::Widgets::Count.new("Roles", @account_summary[:roles])) %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= render(LesliView::Widgets::Count.new("Users", @account_summary[:users])) %>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<%
|
|
2
|
+
sensitive_key = setting_key.to_s.match?(/password|secret|token|api[_-]?key|private[_-]?key|credential|encryption|salt/i)
|
|
3
|
+
%>
|
|
4
|
+
|
|
5
|
+
<% if sensitive_key %>
|
|
6
|
+
<span class="inline-flex items-center gap-1.5 rounded-full bg-slate-200 px-2.5 py-1 text-xs font-semibold text-slate-600">
|
|
7
|
+
<span class="material-symbols-outlined text-sm" aria-hidden="true">lock</span>
|
|
8
|
+
Filtered
|
|
9
|
+
</span>
|
|
10
|
+
<% elsif value.is_a?(Hash) %>
|
|
11
|
+
<dl class="space-y-2">
|
|
12
|
+
<% value.each do |nested_key, nested_value| %>
|
|
13
|
+
<div class="rounded-lg border border-slate-200 bg-white p-3">
|
|
14
|
+
<dt class="text-xs font-medium text-slate-500"><%= nested_key.to_s.humanize %></dt>
|
|
15
|
+
<dd class="mt-1.5">
|
|
16
|
+
<%= render("configuration_value", setting_key: nested_key, value: nested_value) %>
|
|
17
|
+
</dd>
|
|
18
|
+
</div>
|
|
19
|
+
<% end %>
|
|
20
|
+
</dl>
|
|
21
|
+
<% elsif value.is_a?(Array) %>
|
|
22
|
+
<% if value.empty? %>
|
|
23
|
+
<span class="text-slate-400">Empty list</span>
|
|
24
|
+
<% else %>
|
|
25
|
+
<ul class="flex flex-wrap gap-2">
|
|
26
|
+
<% value.each do |item| %>
|
|
27
|
+
<li class="rounded-lg border border-slate-200 bg-white px-2.5 py-1 text-xs text-slate-700">
|
|
28
|
+
<%= render("configuration_value", setting_key: :item, value: item) %>
|
|
29
|
+
</li>
|
|
30
|
+
<% end %>
|
|
31
|
+
</ul>
|
|
32
|
+
<% end %>
|
|
33
|
+
<% elsif value == true %>
|
|
34
|
+
<span class="inline-flex items-center gap-1.5 rounded-full bg-emerald-100 px-2.5 py-1 text-xs font-semibold text-emerald-700">
|
|
35
|
+
<span class="h-1.5 w-1.5 rounded-full bg-emerald-500" aria-hidden="true"></span>
|
|
36
|
+
Enabled
|
|
37
|
+
</span>
|
|
38
|
+
<% elsif value == false %>
|
|
39
|
+
<span class="inline-flex items-center gap-1.5 rounded-full bg-red-100 px-2.5 py-1 text-xs font-semibold text-red-700">
|
|
40
|
+
<span class="h-1.5 w-1.5 rounded-full bg-red-500" aria-hidden="true"></span>
|
|
41
|
+
Disabled
|
|
42
|
+
</span>
|
|
43
|
+
<% elsif value.nil? %>
|
|
44
|
+
<span class="italic text-slate-400">Not configured</span>
|
|
45
|
+
<% elsif value.to_s.match?(/\A#[0-9a-f]{3,8}\z/i) %>
|
|
46
|
+
<span class="inline-flex items-center gap-2">
|
|
47
|
+
<span class="h-5 w-5 rounded-md border border-slate-300 shadow-sm" style="background-color: <%= value %>" aria-hidden="true"></span>
|
|
48
|
+
<code class="break-all rounded-md bg-slate-200 px-2 py-1 text-xs text-slate-700"><%= value %></code>
|
|
49
|
+
</span>
|
|
50
|
+
<% else %>
|
|
51
|
+
<code class="break-all rounded-md bg-slate-200 px-2 py-1 text-xs leading-5 text-slate-700"><%= value %></code>
|
|
52
|
+
<% end %>
|