lesli 5.0.4 → 5.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/lesli/brand/app-logo2.svg +52 -0
  3. data/app/assets/javascripts/lesli/users/passwords.js +3 -3
  4. data/app/assets/javascripts/lesli/users/registrations.js +3 -3
  5. data/app/assets/javascripts/lesli/users/sessions.js +3 -3
  6. data/app/controllers/lesli/application_controller.rb +3 -3
  7. data/app/controllers/lesli/application_lesli_controller.rb +2 -2
  8. data/app/controllers/lesli/interfaces/application/authorization.rb +1 -1
  9. data/app/controllers/lesli/interfaces/application/requester.rb +1 -1
  10. data/app/controllers/lesli/shared/dashboards_controller.rb +308 -0
  11. data/app/controllers/users/confirmations_controller.rb +1 -1
  12. data/app/controllers/users/passwords_controller.rb +7 -10
  13. data/app/helpers/lesli/general_helper.rb +1 -1
  14. data/app/helpers/lesli/navigation_helper.rb +17 -16
  15. data/app/lib/lesli/system.rb +2 -1
  16. data/app/mailers/lesli/devise_mailer.rb +1 -1
  17. data/app/models/concerns/account_initializer.rb +9 -0
  18. data/app/models/lesli/account.rb +1 -0
  19. data/app/models/lesli/application_lesli_record.rb +1 -1
  20. data/app/models/lesli/shared/dashboard.rb +162 -0
  21. data/app/models/lesli/system_controller.rb +1 -0
  22. data/app/operators/lesli/controller_operator.rb +148 -0
  23. data/app/views/devise/passwords/new.html.erb +1 -1
  24. data/app/views/lesli/partials/_application-lesli-engines.html.erb +1 -1
  25. data/app/views/lesli/partials/_application-lesli-icons.html.erb +1 -1
  26. data/config/locales/translations.en.yml +17 -0
  27. data/config/locales/translations.es.yml +17 -0
  28. data/config/routes.rb +4 -2
  29. data/db/seed/development/users.rb +0 -1
  30. data/db/seeds.rb +16 -29
  31. data/lib/lesli/version.rb +1 -1
  32. data/lib/mailer_previews/devise_mailer_preview.rb +7 -0
  33. data/lib/sass/lesli/layouts/application-navbar.scss +1 -1
  34. data/lib/tasks/lesli/controllers.rake +1 -91
  35. data/lib/tasks/lesli/db.rake +36 -6
  36. data/lib/tasks/lesli/dev.rake +66 -0
  37. data/lib/tasks/lesli/engine.rake +59 -0
  38. data/lib/tasks/lesli/{role.rake → privileges.rake} +3 -3
  39. data/lib/tasks/lesli_tasks.rake +5 -0
  40. data/lib/vue/application.js +2 -1
  41. data/lib/vue/devise/passwords.js +8 -8
  42. data/lib/vue/devise/registrations.js +2 -2
  43. data/lib/vue/devise/sessions.js +11 -6
  44. data/lib/vue/layouts/application-header.vue +6 -1
  45. data/lib/vue/shared/dashboards/apps/edit.vue +215 -0
  46. data/lib/vue/{apps → shared}/dashboards/apps/index.vue +3 -5
  47. data/lib/vue/{apps → shared}/dashboards/apps/show.vue +26 -16
  48. data/lib/vue/{apps → shared}/dashboards/components/form.vue +31 -43
  49. data/lib/vue/shared/stores/dashboard.js +251 -0
  50. data/lib/vue/stores/translations.json +24 -72
  51. data/lib/vue/stores/{user.js → users.js} +1 -1
  52. data/lib/webpack/base.js +3 -2
  53. data/readme.md +1 -1
  54. metadata +46 -52
  55. data/lib/vue/apps/dashboards/apps/edit.vue +0 -105
  56. data/lib/vue/apps/dashboards/components/preview.vue +0 -172
  57. /data/app/assets/icons/lesli/{cloud-vault.svg → cloud-guard.svg} +0 -0
  58. /data/lib/vue/{apps → shared}/cloudobjects/action.vue +0 -0
  59. /data/lib/vue/{apps → shared}/cloudobjects/discussion/content.vue +0 -0
  60. /data/lib/vue/{apps → shared}/cloudobjects/discussion/element.vue +0 -0
  61. /data/lib/vue/{apps → shared}/cloudobjects/discussion/filters.vue +0 -0
  62. /data/lib/vue/{apps → shared}/cloudobjects/discussion/new.vue +0 -0
  63. /data/lib/vue/{apps → shared}/cloudobjects/discussion.vue +0 -0
  64. /data/lib/vue/{apps → shared}/cloudobjects/file/grid.vue +0 -0
  65. /data/lib/vue/{apps → shared}/cloudobjects/file/list.vue +0 -0
  66. /data/lib/vue/{apps → shared}/cloudobjects/file.vue +0 -0
  67. /data/lib/vue/{apps → shared}/dashboards/apps/new.vue +0 -0
  68. /data/lib/vue/{apps → shared}/workflows2/apps/actions/form.vue +0 -0
  69. /data/lib/vue/{apps → shared}/workflows2/apps/actions/forms/chatroom-form.vue +0 -0
  70. /data/lib/vue/{apps → shared}/workflows2/apps/actions/forms/cloud-object-clone-form.vue +0 -0
  71. /data/lib/vue/{apps → shared}/workflows2/apps/actions/forms/cloud-object-file-form.vue +0 -0
  72. /data/lib/vue/{apps → shared}/workflows2/apps/actions/forms/email-form.vue +0 -0
  73. /data/lib/vue/{apps → shared}/workflows2/apps/actions/forms/notification-form.vue +0 -0
  74. /data/lib/vue/{apps → shared}/workflows2/apps/actions/forms/send-cloud-object-file.vue +0 -0
  75. /data/lib/vue/{apps → shared}/workflows2/apps/actions/forms/task-form.vue +0 -0
  76. /data/lib/vue/{apps → shared}/workflows2/apps/actions/index.vue +0 -0
  77. /data/lib/vue/{apps → shared}/workflows2/apps/checks/form.vue +0 -0
  78. /data/lib/vue/{apps → shared}/workflows2/apps/checks/index.vue +0 -0
  79. /data/lib/vue/{apps → shared}/workflows2/apps/index.vue +0 -0
  80. /data/lib/vue/{apps → shared}/workflows2/apps/new.vue +0 -0
  81. /data/lib/vue/{apps → shared}/workflows2/apps/show.vue +0 -0
  82. /data/lib/vue/{apps → shared}/workflows2/components/associations.vue +0 -0
  83. /data/lib/vue/{apps → shared}/workflows2/components/chart.vue +0 -0
  84. /data/lib/vue/{apps → shared}/workflows2/components/workflow-form.vue +0 -0
  85. /data/lib/vue/{apps → shared}/workflows2/components/workflow-status-dropdown.vue +0 -0
@@ -0,0 +1,308 @@
1
+ =begin
2
+
3
+ Lesli
4
+
5
+ Copyright (c) 2023, Lesli Technologies, S. A.
6
+
7
+ This program is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program. If not, see http://www.gnu.org/licenses/.
19
+
20
+ Lesli · Ruby on Rails SaaS Development Framework.
21
+
22
+ Made with ♥ by https://www.lesli.tech
23
+ Building a better future, one line of code at a time.
24
+
25
+ @contact hello@lesli.tech
26
+ @website https://www.lesli.tech
27
+ @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
+
29
+ // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
+ // ·
31
+ =end
32
+ module Lesli
33
+ module Shared
34
+ class DashboardsController < ApplicationLesliController
35
+ before_action :set_dashboard, only: [:show, :update, :destroy, :options]
36
+
37
+ def index
38
+ respond_to do |format|
39
+ format.html {}
40
+ format.json do
41
+ dynamic_info = self.class.dynamic_info
42
+ model = dynamic_info[:model]
43
+
44
+ dashboards = model.list(current_user, @query)
45
+ respond_with_successful(dashboards)
46
+ end
47
+ end
48
+ end
49
+
50
+ def show
51
+ respond_to do |format|
52
+ format.html {}
53
+ format.json do
54
+ return respond_with_not_found unless @dashboard
55
+ respond_with_successful(@dashboard.show)
56
+ end
57
+ end
58
+ end
59
+
60
+ def new
61
+ end
62
+
63
+ def edit
64
+ end
65
+
66
+ # @controller_action_param :name [String] The name of the new dashboard
67
+ # @controller_action_param :default [Boolean] A flag that marks this dashboard as default or not. The default dashboard is the dashboard all users will have access to on the root page
68
+ # @controller_action_param :main [Boolean] A flag that marks this dashboard as main. Since a user can have multiple dashboards, this is the one they will see on the root page when loging in
69
+ # @controller_action_param :roles_id [Integer] The id of the role that will have access to this dashboard
70
+ # @controller_action_param :user_main_id [Integer] The id of the user
71
+ # @controller_action_param :component_attributes [Array] Array of hashes that represent the attributes of the dashboard components
72
+ # @controller_action_param :component_attributes.name [String] The name of this component given to the user
73
+ # @controller_action_param :component_attributes.component_id [String] An enum value, that indicates which component is being loaded
74
+ # @controller_action_param :component_attributes.layout [Integer] Number from 1 to 12, bigger numbers default to 12, smallest numbers default to 1. The column size that the UI component will use
75
+ # @controller_action_param :component_attributes.index [Integer] The position of the component in the layout
76
+ # @controller_action_param :component_attributes.configuration [Hash] Specific filtering and configuration of the components
77
+ # @return [Json] Json that contains wheter the creation of the dashboard was successful or not.
78
+ # If it is not successful, it returns an error message
79
+ # @description Creates a new dashboard associated to the *current_user*'s *account*
80
+ # @example
81
+ # # Executing this controller's action from javascript's frontend
82
+ # let data = {
83
+ # dashboard: {
84
+ # name: "Sales Dashboard",
85
+ # default: false,
86
+ # roles_id: 3,
87
+ # components_attributes: [
88
+ # {
89
+ # name: 'Projects Count',
90
+ # component_id: projects_component,
91
+ # layout: 4,
92
+ # index: 3,
93
+ # configuration: {}
94
+ # }, {
95
+ # name: 'New Customers Count',
96
+ # component_id: new_customers_component,
97
+ # layout: 4,
98
+ # index: 3,
99
+ # configuration: {}
100
+ # }
101
+ # ]
102
+ # }
103
+ # };
104
+ # this.http.post('127.0.0.1/help/dashboards', data);
105
+ def create
106
+ dynamic_info = self.class.dynamic_info
107
+ module_name = dynamic_info[:module_name]
108
+ model = dynamic_info[:model]
109
+ full_module_name = dynamic_info[:full_module_name].underscore
110
+
111
+ dashboard = model.new(dashboard_params)
112
+ dashboard["#{full_module_name}_account_id".to_sym] = current_user.account.id
113
+ dashboard.user_creator = current_user
114
+
115
+ if dashboard.save
116
+ respond_with_successful(dashboard)
117
+ else
118
+ respond_with_error(dashboard.errors.full_messages.to_sentence)
119
+ end
120
+ end
121
+
122
+ # @controller_action_param :name [String] The name of the new dashboard
123
+ # @controller_action_param :default [Boolean] A flag that marks this dashboard as default or not. The default dashboard is the dashboard all users will have access to on the root page
124
+ # @controller_action_param :main [Boolean] A flag that marks this dashboard as main. Since a user can have multiple dashboards, this is the one they will see on the root page when loging in
125
+ # @controller_action_param :roles_id [Integer] The id of the role that will have access to this dashboard
126
+ # @controller_action_param :user_main_id [Integer] The id of the user
127
+ # @controller_action_param :component_attributes [Array] Array of hashes that represent the attributes of the dashboard components
128
+ # @controller_action_param :component_attributes.name [String] The name of this component given to the user
129
+ # @controller_action_param :component_attributes.component_id [String] An enum value, that indicates which component is being loaded
130
+ # @controller_action_param :component_attributes.layout [Integer] Number from 1 to 12, bigger numbers default to 12, smallest numbers default to 1. The column size that the UI component will use
131
+ # @controller_action_param :component_attributes.index [Integer] The position of the component in the layout
132
+ # @controller_action_param :component_attributes.configuration [Hash] Specific filtering and configuration of the components
133
+ # @return [Json] Json that contains wheter the dashboard was successfully updated or not.
134
+ # If it it not successful, it returns an error message
135
+ # @description Updates an existing dashboard associated to the *current_user*'s *account*.
136
+ # @example
137
+ # # Executing this controller's action from javascript's frontend
138
+ # let dashboard_id = 4;
139
+ # let data = {
140
+ # dashboard: {
141
+ # name: "Sales Dashboard",
142
+ # default: false,
143
+ # roles_id: 3,
144
+ # components_attributes: [
145
+ # {
146
+ # name: 'Projects Count',
147
+ # component_id: projects_component,
148
+ # layout: 4,
149
+ # index: 3,
150
+ # configuration: {}
151
+ # }, {
152
+ # name: 'New Customers Count',
153
+ # component_id: new_customers_component,
154
+ # layout: 4,
155
+ # index: 3,
156
+ # configuration: {}
157
+ # }
158
+ # ]
159
+ # }
160
+ # };
161
+ # this.http.put(`127.0.0.1/help/dashboards/${dashboard_id}`, data);
162
+ def update
163
+ return respond_with_not_found unless @dashboard
164
+
165
+ if @dashboard.update(dashboard_params)
166
+ respond_with_successful(@dashboard.show)
167
+ else
168
+ respond_with_error(@dashboard.errors.full_messages.to_sentence)
169
+ end
170
+ end
171
+
172
+ # @return [Json] Json that contains wheter the dashboard was successfully deleted or not.
173
+ # If it it not successful, it returns an error message
174
+ # @description Deletes an existing *dashboard* associated to the *current_user*'s *account*.
175
+ # Since the dashboard has details, these are also deleted. However, if there
176
+ # is an existing *cloud_object* associated to the *dashboard*, it cannot be deleted
177
+ # @example
178
+ # # Executing this controller's action from javascript's frontend
179
+ # let dashboard_id = 4;
180
+ # this.http.delete(`127.0.0.1/help/dashboards/${dashboard_id}`);
181
+ def destroy
182
+ return respond_with_not_found unless @dashboard
183
+
184
+ if @dashboard.destroy
185
+ respond_with_successful
186
+ else
187
+ respond_with_error(@dashboard.errors.full_messages.to_sentence)
188
+ end
189
+ end
190
+
191
+ def options
192
+ dynamic_info = self.class.dynamic_info
193
+ model = dynamic_info[:module_model]
194
+
195
+ respond_with_successful(model.options(current_user, @query))
196
+ end
197
+
198
+ def resource_component
199
+ dynamic_info = self.class.dynamic_info
200
+ component_model = dynamic_info[:component_model]
201
+
202
+ component_id = sanitize(params[:component_id].to_sym)
203
+
204
+ # We verify if the method exists, and if it is in the available component list
205
+ if component_model.respond_to?(component_id) && component_model.component_ids[component_id]
206
+ respond_with_successful(component_model.public_send(component_id, current_user, @query))
207
+ else
208
+ respond_with_not_found()
209
+ end
210
+ end
211
+
212
+ private
213
+
214
+ # @return [void]
215
+ # @description Sets the variable @dashboard. The variable contains the *cloud_object* *dashboard*
216
+ # to be handled by the controller action that called this method
217
+ # @example
218
+ # #suppose params[:id] = 1
219
+ # puts @dashboard # will display nil
220
+ # set_dashboard
221
+ # puts @dashboard # will display an instance of CloudHelp:TicketDashboard
222
+ def set_dashboard
223
+ dynamic_info = self.class.dynamic_info
224
+
225
+ model = dynamic_info[:module_model]
226
+ module_name = dynamic_info[:module_name]
227
+ module_name_full = dynamic_info[:module_name_full].underscore
228
+
229
+ # When params[:id] is 'default' order of priority is:
230
+ # Main dashboard for user goes first
231
+ # Main dashboard for role goes second
232
+ # Default dashboard goes third
233
+ if params[:id] == "default"
234
+ # Main dashboard for user
235
+ @dashboard = model.find_by(
236
+ #"#{full_module_name}_account_id".to_sym => current_user.account.id,
237
+ account_id: current_user.account.id,
238
+ main: true,
239
+ #user_main_id: current_user.id
240
+ )
241
+
242
+ return if @dashboard
243
+
244
+ # Main dashboard for role
245
+ # @dashboard = model.find_by(
246
+ # :account_id => current_user.account.id,
247
+ # role_id: current_user.roles.first.id
248
+ # )
249
+ # return if @dashboard
250
+
251
+ # Default dashboard
252
+ @dashboard = model.find_by(
253
+ account_id: current_user.account.id,
254
+ default: true
255
+ )
256
+ else
257
+ @dashboard = model.find_by(
258
+ id: params[:id],
259
+ :account_id => current_user.account.id,
260
+ )
261
+ end
262
+ end
263
+
264
+ def dashboard_params
265
+ params.require(:dashboard).permit(
266
+ :name,
267
+ :default,
268
+ :roles_id,
269
+ components_attributes: [
270
+ :id,
271
+ :name,
272
+ :component_id,
273
+ :layout,
274
+ :index,
275
+ {query_configuration: {}},
276
+ {custom_configuration: {}},
277
+ :_destroy
278
+ ]
279
+ )
280
+ end
281
+
282
+ private
283
+
284
+ # Build the Rails models and engine information for
285
+ # the current engine implementing the shared dashboards
286
+ # Example: For the LesliAudit engine
287
+ # {
288
+ # :module_name => "audit",
289
+ # :module_name_full => "LesliAudit",
290
+ # :module_model => "LesliAudit::Dashboard",
291
+ # :module_model_component => "LesliAudit::Dashboard::Component"
292
+ # }
293
+ def self.dynamic_info
294
+
295
+ module_info = self.name.split("::")
296
+
297
+ module_name = module_info[0].sub("Lesli", "").downcase
298
+
299
+ {
300
+ module_name: module_name,
301
+ module_name_full: module_info[0],
302
+ module_model: "#{ module_info[0] }::Dashboard".constantize,
303
+ module_model_component: "#{ module_info[0] }::Dashboard::Component".constantize
304
+ }
305
+ end
306
+ end
307
+ end
308
+ end
@@ -25,7 +25,7 @@ class Users::ConfirmationsController < Devise::ConfirmationsController
25
25
  # register a log with a validation atempt for the user
26
26
  log = user.logs.create({ description: "confirmation_atempt_successful" })
27
27
 
28
- registration_operator = Lesli::User::RegistrationOperator.new(user)
28
+ registration_operator = Lesli::UserRegistrationOperator.new(user)
29
29
 
30
30
  # confirm the user
31
31
  registration_operator.confirm
@@ -34,16 +34,13 @@ class Users::PasswordsController < Devise::PasswordsController
34
34
 
35
35
  #user.logs.create({ title: "password_creation_successful" })
36
36
 
37
- # begin
38
- # #UserMailer.with(user: user, token: token).reset_password_instructions.deliver_now
39
- #super()
40
- Lesli::DeviseMailer.reset_password_instructions(user, token)
41
- respond_with_successful
42
- # rescue => exception
43
- # #Honeybadger.notify(exception)
44
- # respond_with_error(exception.message)
45
- # end
46
-
37
+ begin
38
+ Lesli::DeviseMailer.reset_password_instructions(user, token).deliver_now
39
+ respond_with_successful
40
+ rescue => exception
41
+ #Honeybadger.notify(exception)
42
+ respond_with_error(exception.message)
43
+ end
47
44
  end
48
45
 
49
46
  def update
@@ -35,7 +35,7 @@ module Lesli
35
35
  module GeneralHelper
36
36
  # build a url path to change locales
37
37
  def language_url(locale)
38
- "/language?locale=#{locale}"
38
+ "/lesli/language?locale=#{locale}"
39
39
  end
40
40
 
41
41
  # return flag code according to locale code
@@ -32,6 +32,14 @@ Building a better future, one line of code at a time.
32
32
 
33
33
  module Lesli
34
34
  module NavigationHelper
35
+
36
+ # Prints a separator line
37
+ def navigation_separator
38
+ content_tag(:li) do
39
+ content_tag(:hr)
40
+ end
41
+ end
42
+
35
43
  # Prints a html link inside a list item
36
44
  def navigation_item(path, label, icon = nil, reload: false)
37
45
  # default vue router links for single page applications
@@ -58,15 +66,9 @@ module Lesli
58
66
  end
59
67
  end
60
68
 
61
- # Prints a separator line
62
- def navigation_separator
63
- content_tag(:li) do
64
- content_tag(:hr)
65
- end
66
- end
67
-
68
69
  # 00.00 System administration
69
70
  def navigation_engine_admin(title: "Administration", subtitle: "Users, privileges, access roles.")
71
+ return unless defined? LesliAdmin
70
72
  navigation_engine_item(title, subtitle, "admin", lesli_admin.root_path, controller_path.include?("lesli_admin"))
71
73
  end
72
74
 
@@ -243,10 +245,10 @@ module Lesli
243
245
 
244
246
  # 07.02 Help engine
245
247
  def navigation_engine_help(title: "Help", subtitle: "Support Ticket System")
246
- return unless defined? CloudHelp
248
+ return unless defined? LesliHelp
247
249
 
248
- navigation_engine_item(title, subtitle, "help", cloud_help.root_path,
249
- controller_path.include?("cloud_help"))
250
+ navigation_engine_item(title, subtitle, "help", lesli_help.root_path,
251
+ controller_path.include?("lesli_help"))
250
252
  end
251
253
 
252
254
  # 07.03 Portal engine
@@ -267,12 +269,11 @@ module Lesli
267
269
 
268
270
  # SECURITY & PRIVACY
269
271
 
270
- # 08.01 Vault engine
271
- def navigation_engine_vault(title: "Vault", subtitle: "")
272
- return unless defined? LesliVault
273
-
274
- navigation_engine_item(title, subtitle, "vault", lesli_vault.root_path,
275
- controller_path.include?("lesli_vault"))
272
+ # 08.01 Guard engine
273
+ def navigation_engine_guard(title: "Guard", subtitle: "Users, privileges and access roles.")
274
+ return unless defined? LesliGuard
275
+ navigation_engine_item(title, subtitle, "guard", lesli_guard.root_path,
276
+ controller_path.include?("lesli_guard"))
276
277
  end
277
278
 
278
279
  # 08.03 Audit engine
@@ -88,7 +88,8 @@ module Lesli
88
88
  "LesliAudit",
89
89
  "LesliBell",
90
90
  "LesliDriver",
91
- "LesliVault"
91
+ "LesliGuard",
92
+ "LesliHelp"
92
93
  ]
93
94
  end
94
95
  end
@@ -17,7 +17,7 @@ module Lesli
17
17
  }
18
18
 
19
19
  # send email
20
- email(
20
+ pp email(
21
21
  params,
22
22
  to: user.email,
23
23
  subject: email_subject,
@@ -79,6 +79,15 @@ module AccountInitializer
79
79
  end
80
80
  end
81
81
 
82
+ # 07.02 LesliHelp - Support Ticket System
83
+ if defined? LesliHelp
84
+ if self.help.blank?
85
+ self.help = LesliHelp::Account.new
86
+ self.help.account = self
87
+ self.help.save!
88
+ end
89
+ end
90
+
82
91
  # 08.03 LesliAudit - System analytics
83
92
  if defined? LesliAudit
84
93
  if self.audit.blank?
@@ -54,6 +54,7 @@ module Lesli
54
54
  has_many :currencies
55
55
  has_many :logs
56
56
 
57
+ has_one :help, class_name: "LesliHelp::Account"
57
58
  has_one :audit, class_name: "LesliAudit::Account"
58
59
  has_one :admin, class_name: "LesliAdmin::Account"
59
60
  has_one :driver, class_name: "LesliDriver::Account"
@@ -31,7 +31,7 @@ Building a better future, one line of code at a time.
31
31
  =end
32
32
 
33
33
  module Lesli
34
- class ApplicationLesliRecord < ApplicationRecord
34
+ class ApplicationLesliRecord < ActiveRecord::Base
35
35
  self.abstract_class = true
36
36
  acts_as_paranoid
37
37
  end
@@ -0,0 +1,162 @@
1
+ =begin
2
+
3
+ Lesli
4
+
5
+ Copyright (c) 2023, Lesli Technologies, S. A.
6
+
7
+ This program is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program. If not, see http://www.gnu.org/licenses/.
19
+
20
+ Lesli · Your Smart Business Assistant.
21
+
22
+ Made with ♥ by https://www.lesli.tech
23
+ Building a better future, one line of code at a time.
24
+
25
+ @contact hello@lesli.tech
26
+ @website https://lesli.tech
27
+ @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
+
29
+ // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
+ // ·
31
+
32
+ =end
33
+
34
+ module Lesli
35
+ module Shared
36
+ class Dashboard < ::Lesli::ApplicationLesliRecord
37
+ self.abstract_class = true
38
+
39
+ belongs_to :user_creator, class_name: "User", foreign_key: "users_id", optional: true
40
+
41
+ after_update :verify_default_dashboard
42
+ after_create :verify_default_dashboard
43
+
44
+ enum component_ids: {}
45
+
46
+ # @return [void]
47
+ # @param account [LesliEngine::Account]
48
+ # @description Initializes a default dummy dashboard for the account. This guarantees that the users
49
+ # will be able to access the dashboard page. Even if it's empty.
50
+ # @example
51
+ # # Imagine you are adding a new engine to your instance (CloudProposal)
52
+ # # To execute this function, you must only do
53
+ # my_account = Account.first
54
+ # my_account.proposal = CloudProposal::Account.new
55
+ def self.initialize_data(account)
56
+ self.create!(
57
+ account: account,
58
+ name: "Default Dashboard",
59
+ default: true,
60
+ main: false
61
+ )
62
+ end
63
+
64
+ # @return [Hash] Hash of containing the information of the dashboard and its components.
65
+ # @description Returns a hash with information about the dashboard and all its *components*.
66
+ # Each component is returned in the configuration view, not the render view. This means that
67
+ # this method is ment to be used when updating the dashboard
68
+ # @example
69
+ # respond_with_successful(CloudHelp::Dashboard.first.show)
70
+ def show
71
+ attributes.merge({
72
+ components: components.order(index: :asc)
73
+ # components: [{
74
+ # name: "ticket",
75
+ # component_id: "ticket"
76
+ # },{
77
+ # name: "ticket",
78
+ # component_id: "ticket"
79
+ # }]
80
+ })
81
+ end
82
+
83
+ # @return [Hash] Hash containing the options to create and manage dashboards
84
+ # @param current_user [User] The user that made this request
85
+ # @param query [Hash] Query containing filters. Currently unused, but required
86
+ # @descriptions Returns a list of options needed to create and manage dashboard components. For now,
87
+ # the returned options are: A list of roles, the enoun containing the component_ids, generic configuration options
88
+ # for all dashboad components and a descriptions hash, that contains brief descriptions for each component to help
89
+ # the user understand what each component does. Any class that inherits from this one can send a block to add extra
90
+ # functionality. For example, the descriptions must be implemented directly from the engine.
91
+ # @example
92
+ # CloudHouse::Dashboard.options(User.find(2), nil)
93
+ def self.options(current_user, query)
94
+ dynamic_info = self.dynamic_info
95
+ component_model = dynamic_info[:module_model_component]
96
+
97
+ component_ids = component_model.component_ids.map do |comp|
98
+ {
99
+ value: comp,
100
+ text: comp
101
+ }
102
+ end
103
+
104
+ options = {
105
+ component_ids: component_ids,
106
+ #components_configuration_options: component_model.configuration_options,
107
+ descriptions: {}
108
+ }
109
+
110
+ if block_given?
111
+ yield(options)
112
+ else
113
+ return options
114
+ end
115
+ end
116
+
117
+ private
118
+
119
+ # @return [void]
120
+ # @descriptions This is an after_updated and after_create method that validates that at any moment in time, there is only
121
+ # one default dashboard in the engine. If there is another default dashboard, it's *default* field is set to false
122
+ # before committing the changes
123
+ # @example
124
+ # CloudFocus::Dasbhoard.where(default: false).first.update!(default: true)
125
+ # # This will automatically trigger this function and remove the *default* field from the old default dashboard
126
+ def verify_default_dashboard
127
+ if default
128
+ dashboards = self.class.where.not(id: id).where(account: account)
129
+ self.class.where.not(id: id).where(account: account).update_all(default: false)
130
+ end
131
+
132
+ unless self.class.where(account: account).find_by(default: true)
133
+ errors.add(:base, I18n.t("core.dashboards.messages_danger_default_dashboard_must_exist"))
134
+ raise ActiveRecord::Rollback
135
+ end
136
+ end
137
+
138
+ # Build the Rails models and engine information for
139
+ # the current engine implementing the shared dashboards
140
+ # Example: For the LesliAudit engine
141
+ # {
142
+ # :module_name => "audit",
143
+ # :module_name_full => "LesliAudit",
144
+ # :module_model => "LesliAudit::Dashboard",
145
+ # :module_model_component => "LesliAudit::Dashboard::Component"
146
+ # }
147
+ def self.dynamic_info
148
+
149
+ module_info = self.name.split("::")
150
+
151
+ module_name = module_info[0].sub("Lesli", "").downcase
152
+
153
+ {
154
+ module_name: module_name,
155
+ module_name_full: module_info[0],
156
+ module_model: "#{ module_info[0] }::Dashboard".constantize,
157
+ module_model_component: "#{ module_info[0] }::Dashboard::Component".constantize
158
+ }
159
+ end
160
+ end
161
+ end
162
+ end
@@ -85,6 +85,7 @@ module Lesli
85
85
  cc[engine][controller] = {
86
86
  id: c[:controller_id],
87
87
  name: c[:controller_name],
88
+ route: c[:route],
88
89
  actions: []
89
90
  }
90
91
  end