fat_free_crm 0.19.2 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fat_free_crm might be problematic. Click here for more details.

Files changed (226) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/brakeman-analysis.yml +46 -0
  3. data/.github/workflows/rubocop-analysis.yml +40 -0
  4. data/.github/workflows/ruby.yml +52 -0
  5. data/.rubocop_todo.yml +49 -43
  6. data/.travis.yml +1 -20
  7. data/CHANGELOG.md +13 -0
  8. data/Gemfile +9 -5
  9. data/Gemfile.lock +163 -119
  10. data/README.md +4 -3
  11. data/SECURITY.md +11 -0
  12. data/app/assets/javascripts/application.js.erb +3 -0
  13. data/app/assets/stylesheets/application.css.erb +1 -0
  14. data/app/assets/stylesheets/base.scss +9 -2
  15. data/app/assets/stylesheets/bootstrap-custom.scss +206 -0
  16. data/app/assets/stylesheets/common.scss +87 -67
  17. data/app/assets/stylesheets/header.scss +0 -8
  18. data/app/controllers/admin/field_groups_controller.rb +1 -1
  19. data/app/controllers/admin/fields_controller.rb +1 -1
  20. data/app/controllers/admin/groups_controller.rb +1 -1
  21. data/app/controllers/admin/tags_controller.rb +2 -2
  22. data/app/controllers/admin/users_controller.rb +2 -1
  23. data/app/controllers/application_controller.rb +10 -0
  24. data/app/controllers/comments_controller.rb +1 -1
  25. data/app/controllers/entities/accounts_controller.rb +1 -1
  26. data/app/controllers/entities/campaigns_controller.rb +1 -1
  27. data/app/controllers/home_controller.rb +3 -1
  28. data/app/controllers/lists_controller.rb +1 -1
  29. data/app/controllers/tasks_controller.rb +3 -3
  30. data/app/controllers/users_controller.rb +3 -1
  31. data/app/helpers/accounts_helper.rb +12 -0
  32. data/app/helpers/application_helper.rb +3 -5
  33. data/app/helpers/tasks_helper.rb +2 -2
  34. data/app/models/entities/account.rb +1 -1
  35. data/app/models/entities/account_contact.rb +1 -1
  36. data/app/models/entities/account_opportunity.rb +1 -1
  37. data/app/models/entities/campaign.rb +1 -1
  38. data/app/models/entities/contact.rb +1 -1
  39. data/app/models/entities/lead.rb +1 -1
  40. data/app/models/entities/opportunity.rb +1 -1
  41. data/app/models/fields/custom_field_pair.rb +2 -2
  42. data/app/models/observers/opportunity_observer.rb +1 -1
  43. data/app/models/polymorphic/address.rb +1 -1
  44. data/app/models/polymorphic/comment.rb +1 -1
  45. data/app/models/polymorphic/email.rb +1 -1
  46. data/app/models/polymorphic/task.rb +1 -1
  47. data/app/models/setting.rb +4 -2
  48. data/app/models/users/preference.rb +2 -2
  49. data/app/models/users/user.rb +2 -2
  50. data/app/views/accounts/_edit.html.haml +2 -2
  51. data/app/views/accounts/_new.html.haml +2 -2
  52. data/app/views/accounts/_sidebar_index.html.haml +2 -2
  53. data/app/views/accounts/_sidebar_show.html.haml +19 -19
  54. data/app/views/accounts/create.js.haml +1 -2
  55. data/app/views/accounts/destroy.js.haml +1 -1
  56. data/app/views/accounts/show.html.haml +1 -0
  57. data/app/views/accounts/update.js.haml +2 -3
  58. data/app/views/admin/field_groups/create.js.haml +0 -1
  59. data/app/views/admin/field_groups/destroy.js.haml +0 -1
  60. data/app/views/admin/field_groups/update.js.haml +0 -1
  61. data/app/views/admin/fields/create.js.haml +0 -1
  62. data/app/views/admin/fields/destroy.js.haml +0 -1
  63. data/app/views/admin/fields/update.js.haml +0 -1
  64. data/app/views/admin/groups/create.js.haml +0 -1
  65. data/app/views/admin/groups/destroy.js.haml +0 -1
  66. data/app/views/admin/groups/update.js.haml +0 -1
  67. data/app/views/admin/tags/create.js.haml +0 -1
  68. data/app/views/admin/tags/destroy.js.haml +0 -1
  69. data/app/views/admin/tags/update.js.haml +0 -1
  70. data/app/views/admin/users/create.js.haml +0 -1
  71. data/app/views/admin/users/destroy.js.haml +0 -1
  72. data/app/views/admin/users/update.js.haml +0 -1
  73. data/app/views/campaigns/_edit.html.haml +1 -1
  74. data/app/views/campaigns/_new.html.haml +1 -1
  75. data/app/views/campaigns/_sidebar_index.html.haml +2 -2
  76. data/app/views/campaigns/_sidebar_show.html.haml +68 -67
  77. data/app/views/campaigns/create.js.haml +1 -2
  78. data/app/views/campaigns/destroy.js.haml +1 -1
  79. data/app/views/campaigns/show.html.haml +1 -0
  80. data/app/views/campaigns/update.js.haml +3 -4
  81. data/app/views/comments/_edit.html.haml +1 -1
  82. data/app/views/comments/_new.html.haml +1 -1
  83. data/app/views/comments/update.js.haml +0 -1
  84. data/app/views/contacts/_edit.html.haml +1 -1
  85. data/app/views/contacts/_new.html.haml +1 -1
  86. data/app/views/contacts/_sidebar_show.html.haml +18 -18
  87. data/app/views/contacts/create.js.haml +0 -1
  88. data/app/views/contacts/destroy.js.haml +1 -1
  89. data/app/views/contacts/show.html.haml +1 -0
  90. data/app/views/contacts/update.js.haml +2 -3
  91. data/app/views/entities/_title_bar.html.haml +1 -1
  92. data/app/views/entities/attach.js.haml +2 -2
  93. data/app/views/entities/discard.js.haml +2 -2
  94. data/app/views/home/_opportunity.html.haml +1 -1
  95. data/app/views/home/_task.html.haml +1 -1
  96. data/app/views/home/index.xls.builder +51 -0
  97. data/app/views/layouts/_sidebar.html.haml +2 -2
  98. data/app/views/layouts/_tabbed.html.haml +14 -11
  99. data/app/views/layouts/application.html.haml +2 -2
  100. data/app/views/leads/_edit.html.haml +2 -2
  101. data/app/views/leads/_new.html.haml +2 -2
  102. data/app/views/leads/_sidebar_index.html.haml +2 -2
  103. data/app/views/leads/_sidebar_show.html.haml +30 -30
  104. data/app/views/leads/create.js.haml +2 -3
  105. data/app/views/leads/destroy.js.haml +2 -2
  106. data/app/views/leads/promote.js.haml +3 -4
  107. data/app/views/leads/reject.js.haml +3 -3
  108. data/app/views/leads/show.html.haml +1 -1
  109. data/app/views/leads/update.js.haml +4 -5
  110. data/app/views/lists/create.js.haml +0 -1
  111. data/app/views/opportunities/_edit.html.haml +1 -1
  112. data/app/views/opportunities/_index_brief.html.haml +1 -1
  113. data/app/views/opportunities/_index_long.html.haml +1 -1
  114. data/app/views/opportunities/_new.html.haml +1 -1
  115. data/app/views/opportunities/_sidebar_index.html.haml +2 -2
  116. data/app/views/opportunities/_sidebar_show.html.haml +47 -46
  117. data/app/views/opportunities/create.js.haml +3 -4
  118. data/app/views/opportunities/destroy.js.haml +3 -3
  119. data/app/views/opportunities/show.html.haml +1 -0
  120. data/app/views/opportunities/update.js.haml +5 -6
  121. data/app/views/shared/_inline_styles.html.haml +0 -26
  122. data/app/views/tasks/_assigned.html.haml +1 -1
  123. data/app/views/tasks/_completed.html.haml +1 -1
  124. data/app/views/tasks/_edit.html.haml +1 -1
  125. data/app/views/tasks/_new.html.haml +1 -1
  126. data/app/views/tasks/_pending.html.haml +1 -1
  127. data/app/views/tasks/_related.html.haml +1 -1
  128. data/app/views/tasks/_selector.html.haml +7 -8
  129. data/app/views/tasks/_sidebar_index.html.haml +2 -2
  130. data/app/views/tasks/_title.html.haml +1 -1
  131. data/app/views/tasks/complete.js.haml +1 -1
  132. data/app/views/tasks/create.js.haml +1 -2
  133. data/app/views/tasks/destroy.js.haml +1 -1
  134. data/app/views/tasks/uncomplete.js.haml +1 -2
  135. data/app/views/tasks/update.js.haml +0 -1
  136. data/app/views/users/change_password.js.haml +0 -1
  137. data/app/views/users/update.js.haml +0 -1
  138. data/app/views/users/upload_avatar.js.haml +0 -1
  139. data/app/views/versions/_version_item.html.haml +1 -1
  140. data/bin/setup +6 -6
  141. data/config/application.rb +1 -1
  142. data/config/environment.rb +1 -1
  143. data/config/initializers/content_security_policy.rb +5 -0
  144. data/config/initializers/new_framework_defaults_6_0.rb +46 -0
  145. data/config/initializers/simple_form.rb +135 -55
  146. data/config/initializers/simple_form_bootstrap.rb +433 -0
  147. data/config/locales/fat_free_crm.cs.yml +86 -47
  148. data/config/locales/fat_free_crm.ru.yml +1 -0
  149. data/config/settings.default.yml +0 -41
  150. data/db/migrate/20200806004152_add_pattern_to_fields.rb +7 -0
  151. data/db/migrate/20200806004459_add_html5_to_fields.rb +10 -0
  152. data/db/schema.rb +11 -6
  153. data/fat_free_crm.gemspec +4 -4
  154. data/lib/fat_free_crm/core_ext.rb +1 -1
  155. data/lib/fat_free_crm/gem_ext.rb +0 -1
  156. data/lib/fat_free_crm/tabs.rb +2 -2
  157. data/lib/fat_free_crm/version.rb +2 -2
  158. data/lib/gravatar_image_tag.rb +1 -1
  159. data/lib/tasks/ffcrm/preference_update.rake +19 -0
  160. data/lib/tasks/ffcrm/setup.rake +1 -1
  161. data/lib/templates/erb/scaffold/_form.html.erb +4 -2
  162. data/spec/controllers/comments_controller_spec.rb +6 -6
  163. data/spec/controllers/home_controller_spec.rb +3 -3
  164. data/spec/controllers/users_controller_spec.rb +13 -1
  165. data/spec/factories/user_factories.rb +5 -2
  166. data/spec/features/acceptance_helper.rb +1 -1
  167. data/spec/features/accounts_spec.rb +2 -2
  168. data/spec/features/admin/groups_spec.rb +1 -1
  169. data/spec/features/admin/users_spec.rb +1 -1
  170. data/spec/features/campaigns_spec.rb +2 -2
  171. data/spec/features/contacts_spec.rb +2 -5
  172. data/spec/features/leads_spec.rb +11 -7
  173. data/spec/features/opportunities_spec.rb +4 -4
  174. data/spec/features/support/browser.rb +6 -2
  175. data/spec/features/support/selector_helpers.rb +10 -1
  176. data/spec/features/tasks_spec.rb +6 -6
  177. data/spec/helpers/accounts_helper_spec.rb +57 -0
  178. data/spec/models/entities/opportunity_spec.rb +2 -2
  179. data/spec/models/fields/custom_field_pair_spec.rb +2 -2
  180. data/spec/models/observers/entity_observer_spec.rb +3 -3
  181. data/spec/models/polymorphic/task_spec.rb +11 -11
  182. data/spec/models/polymorphic/version_spec.rb +2 -2
  183. data/spec/models/users/preference_spec.rb +1 -1
  184. data/spec/views/accounts/_edit.haml_spec.rb +1 -1
  185. data/spec/views/accounts/_new.haml_spec.rb +1 -1
  186. data/spec/views/accounts/create.js.haml_spec.rb +0 -1
  187. data/spec/views/accounts/update.js.haml_spec.rb +2 -5
  188. data/spec/views/admin/field_groups/create.js.haml_spec.rb +0 -1
  189. data/spec/views/admin/field_groups/update.js.haml_spec.rb +0 -1
  190. data/spec/views/admin/users/create.js.haml_spec.rb +0 -1
  191. data/spec/views/admin/users/destroy.js.haml_spec.rb +0 -6
  192. data/spec/views/admin/users/update.js.haml_spec.rb +1 -2
  193. data/spec/views/campaigns/_edit.haml_spec.rb +1 -1
  194. data/spec/views/campaigns/_new.haml_spec.rb +1 -1
  195. data/spec/views/campaigns/create.js.haml_spec.rb +0 -1
  196. data/spec/views/campaigns/destroy.js.haml_spec.rb +0 -1
  197. data/spec/views/campaigns/update.js.haml_spec.rb +2 -6
  198. data/spec/views/contacts/_edit.haml_spec.rb +1 -1
  199. data/spec/views/contacts/_new.haml_spec.rb +1 -1
  200. data/spec/views/contacts/create.js.haml_spec.rb +0 -1
  201. data/spec/views/contacts/update.js.haml_spec.rb +3 -7
  202. data/spec/views/home/index.haml_spec.rb +2 -2
  203. data/spec/views/leads/_edit.haml_spec.rb +1 -1
  204. data/spec/views/leads/_new.haml_spec.rb +1 -1
  205. data/spec/views/leads/create.js.haml_spec.rb +0 -3
  206. data/spec/views/leads/destroy.js.haml_spec.rb +0 -2
  207. data/spec/views/leads/promote.js.haml_spec.rb +3 -11
  208. data/spec/views/leads/reject.js.haml_spec.rb +0 -3
  209. data/spec/views/leads/update.js.haml_spec.rb +3 -11
  210. data/spec/views/opportunities/_edit.haml_spec.rb +1 -1
  211. data/spec/views/opportunities/_new.haml_spec.rb +1 -1
  212. data/spec/views/opportunities/create.js.haml_spec.rb +0 -2
  213. data/spec/views/opportunities/destroy.js.haml_spec.rb +0 -3
  214. data/spec/views/opportunities/update.js.haml_spec.rb +3 -11
  215. data/spec/views/tasks/_edit.haml_spec.rb +1 -1
  216. data/spec/views/tasks/complete.js.haml_spec.rb +0 -1
  217. data/spec/views/tasks/create.js.haml_spec.rb +0 -2
  218. data/spec/views/tasks/destroy.js.haml_spec.rb +0 -1
  219. data/spec/views/tasks/uncomplete.js.haml_spec.rb +0 -1
  220. data/spec/views/tasks/update.js.haml_spec.rb +1 -4
  221. data/spec/views/users/change_password.js.haml_spec.rb +1 -2
  222. data/spec/views/users/update.js.haml_spec.rb +1 -2
  223. data/spec/views/users/upload_avatar.js.haml_spec.rb +1 -2
  224. metadata +23 -14
  225. data/config/initializers/new_framework_defaults_5_2.rb +0 -40
  226. data/lib/fat_free_crm/gem_ext/simple_form/action_view_extensions/form_helper.rb +0 -24
@@ -3,7 +3,7 @@
3
3
 
4
4
  $('##{dom_id(@entity)}').css('background-color', '#ffe4e1').slideUp(250);
5
5
  - if called_from_index_page?
6
- = refresh_sidebar(:index, :filters)
6
+ = refresh_sidebar(:index)
7
7
  $('#paginate').replaceWith('#{ j render(partial: "shared/paginate_with_per_page") }');
8
8
  - elsif called_from_landing_page?("campaigns")
9
- = refresh_sidebar_for(:campaigns, :show, :summary)
9
+ = refresh_sidebar_for(:campaigns, :show)
@@ -6,14 +6,14 @@
6
6
  - if called_from_landing_page?
7
7
  crm.flip_form('convert_#{entity_name}');
8
8
  crm.set_title('convert_#{entity_name}', '#{h @entity.full_name}');
9
- = refresh_sidebar(:show, :summary)
9
+ = refresh_sidebar(:show)
10
10
  - else
11
11
  $('##{id}').replaceWith('#{ j render(partial: entity_name, collection: [ @entity ]) }');
12
12
  $('##{id}').effect("highlight", { duration:1500 });
13
13
  - if called_from_index_page?
14
- = refresh_sidebar(:index, :filters)
14
+ = refresh_sidebar(:index)
15
15
  - elsif called_from_landing_page?("campaigns")
16
- = refresh_sidebar_for(:campaigns, :show, :summary)
16
+ = refresh_sidebar_for(:campaigns, :show)
17
17
  - if @opportunity.id # Make sure the opportunity has been saved.
18
18
  $('#opportunities').prepend('#{ j render(partial: "opportunities/opportunity", collection: [ @opportunity ]) }');
19
19
  - else
@@ -22,5 +22,4 @@
22
22
  - else
23
23
  $('##{id}').html('#{ j render(partial: "convert") }');
24
24
  crm.create_or_select_account(#{ request.referer =~ /\/accounts\// || @account.id.blank? });
25
- $('##{id}').effect("shake", { duration:250, distance: 6 });
26
25
  $('##{dom_id(@entity, :edit)} input[type!=hidden]').first().focus();
@@ -5,8 +5,8 @@
5
5
  $('##{id}').replaceWith('#{ j render(partial: entity_name, collection: [ @entity ]) }');
6
6
  $('##{id}').effect("highlight", { duration:1500 });
7
7
  - if called_from_index_page?
8
- = refresh_sidebar(:index, :filters)
8
+ = refresh_sidebar(:index)
9
9
  - elsif called_from_landing_page?("campaigns")
10
- = refresh_sidebar_for(:campaigns, :show, :summary)
10
+ = refresh_sidebar_for(:campaigns, :show)
11
11
  - else
12
- = refresh_sidebar(:show, :summary)
12
+ = refresh_sidebar(:show)
@@ -7,7 +7,7 @@
7
7
  = styles_for :task
8
8
 
9
9
  = render 'leads/title_bar', lead: @lead
10
-
10
+ = render 'leads/sidebar_show', lead: @lead
11
11
  = render "comments/new", commentable: @lead
12
12
  = render partial: "shared/timeline", collection: @timeline
13
13
 
@@ -6,23 +6,22 @@
6
6
  - if called_from_landing_page?
7
7
  crm.flip_form('edit_#{entity_name}');
8
8
  crm.set_title('edit_#{entity_name}', '#{h @entity.full_name}');
9
- = refresh_sidebar(:show, :summary)
9
+ = refresh_sidebar(:show)
10
10
  - else
11
11
  $('##{id}').replaceWith('#{ j render(partial: entity_name, collection: [ @entity ]) }');
12
12
  $('##{id}').effect("highlight", { duration:1500 });
13
13
  - if called_from_index_page?
14
- = refresh_sidebar(:index, :filters)
14
+ = refresh_sidebar(:index)
15
15
  - elsif called_from_landing_page?("campaigns")
16
- = refresh_sidebar_for(:campaigns, :show, :summary)
16
+ = refresh_sidebar_for(:campaigns, :show)
17
17
  - else
18
18
  $('#recently').replaceWith('#{ j render(partial: "shared/recently") }');
19
19
 
20
20
  - else
21
21
  $('##{id}').html('#{ j render(partial: "edit") }');
22
- $('##{id}').effect("shake", { duration:250, distance: 6 });
23
22
  - if @entity.errors[:first_name].blank? and entity.errors[:last_name].present?
24
23
  $('#lead_last_name').focus();
25
24
  - else
26
25
  $('##{dom_id(@entity, :edit)} input[type!=hidden]').first().focus();
27
26
 
28
- = hook(:entity_update, self, {entity: @entity})
27
+ = hook(:entity_update, self, {entity: @entity})
@@ -4,6 +4,5 @@
4
4
 
5
5
  - else # Couldn't create the saved list -- validation failed.
6
6
  $form = $('.lists .list_form:visible')
7
- $form.effect("shake", { duration:250, distance: 6 });
8
7
  $form.find("[name='list[name]']").focus();
9
8
  $form.find("[type=submit]").prop('disabled', false);
@@ -13,6 +13,6 @@
13
13
  = hook(:entity_form, self, {f: f, entity: @opportunity})
14
14
 
15
15
  .buttonbar
16
- = f.submit t(:save_opportunity), onclick: %/$("#account_assigned_to").val($("#opportunity_assigned_to").val());/
16
+ = f.submit t(:save_opportunity), onclick: %/$("#account_assigned_to").val($("#opportunity_assigned_to").val());/, class: 'btn btn-primary'
17
17
  #{t :or}
18
18
  = link_to_cancel edit_opportunity_path(@opportunity)
@@ -2,7 +2,7 @@
2
2
  - if opportunity.stage
3
3
  .strip{class: opportunity.stage}= t(opportunity.stage)
4
4
  - else
5
- .strip.white #{t :other}
5
+ .strip.other #{t :other}
6
6
 
7
7
  %ul.tools
8
8
  - if can?(:update, opportunity)
@@ -2,7 +2,7 @@
2
2
  - if opportunity.stage
3
3
  .strip{class: opportunity.stage}= t(opportunity.stage)
4
4
  - else
5
- .strip.white #{t :other}
5
+ .strip.other #{t :other}
6
6
 
7
7
  %ul.tools
8
8
  - if can?(:update, opportunity)
@@ -13,6 +13,6 @@
13
13
  = hook(:entity_form, self, {f: f, entity: @opportunity})
14
14
 
15
15
  .buttonbar
16
- = f.submit t(:create_opportunity), onclick: %/$("#account_assigned_to").val($("#opportunity_assigned_to").val()); if ($("#account_id").css('display') != 'none') { $("#account_id").enable(); }/
16
+ = f.submit t(:create_opportunity), onclick: %/$("#account_assigned_to").val($("#opportunity_assigned_to").val()); if ($("#account_id").css('display') != 'none') { $("#account_id").enable(); }/, class: 'btn btn-primary'
17
17
  #{t :or}
18
18
  = link_to_cancel new_opportunity_path
@@ -1,13 +1,13 @@
1
1
  .filters#filters
2
2
  .caption= t :opportunity_stages
3
3
  - @stage.each do |value, key|
4
- .check_box{style: "border-bottom: 1px silver dotted;"}
4
+ .check_box
5
5
  %div{style: "float:right;"}
6
6
  = @opportunity_stage_total[key]
7
7
  = opportunity_stage_checkbox(key, @opportunity_stage_total[key])
8
8
  = label_tag(key, value)
9
9
 
10
- .check_box{style: "border-bottom: 1px silver dotted;"}
10
+ .check_box
11
11
  %div{style: "float:right;"}
12
12
  = @opportunity_stage_total[:other]
13
13
  = opportunity_stage_checkbox("other", @opportunity_stage_total[:other])
@@ -1,52 +1,53 @@
1
1
  .panel#summary
2
2
  .caption #{t :opportunity_summary}
3
- %li
4
- %dt= @opportunity.stage ? t(@opportunity.stage) : t(:other)
5
- %tt #{t :stage}:
6
- %li
7
- %dt= @opportunity.closes_on ? l(@opportunity.closes_on, format: :mmdd) : t(:n_a)
8
- %tt #{t :close_date}:
9
- - if @opportunity.closes_on && @opportunity.stage !~ /lost|won/
10
- %li
11
- - days_left = (@opportunity.closes_on - Date.today).to_i
12
- - if days_left >= 0
13
- %dt= days_left
14
- %tt #{t :days_left}:
15
- - else
16
- %dt.warn= t('pluralize.day', count: (days_left * -1))
17
- %tt #{t :days_late}:
18
- %li
19
- %dt= @opportunity.probability ? @opportunity.probability.to_s + "%" : t(:n_a)
20
- %tt #{t :probability}:
21
- %li
22
- %dt= @opportunity.amount ? number_to_currency(@opportunity.amount.to_f, precision: 0) : t(:n_a)
23
- %tt #{t :amount}:
24
- %li
25
- %dt= @opportunity.discount ? number_to_currency(@opportunity.discount.to_f, precision: 0) : t(:n_a)
26
- - if @opportunity.amount && @opportunity.discount
27
- %tt== Discount (#{number_to_percentage(@opportunity.discount / @opportunity.amount * 100, precision: 1)}):
28
- - else
29
- %tt #{t :discount}:
30
- %li
31
- %dt= @opportunity.amount ? number_to_currency(@opportunity.weighted_amount.to_f, precision: 0) : t(:n_a)
32
- %tt #{t :weighted_amount}:
33
- %li
34
- %dt= @opportunity.assigned_to ? truncate(@opportunity.assignee.full_name, length: 16) : nil
35
- %tt #{t :assigned_to}:
36
- %li
37
- %dt
38
- - if @opportunity.account
39
- = link_to(truncate(@opportunity.account.name, length: 16), @opportunity.account, title: @opportunity.account.name)
40
- - else
41
- #{t :n_a}
42
- %tt #{t :account}:
43
- %li.last
44
- %dt
45
- - if @opportunity.campaign
46
- = link_to(truncate(@opportunity.campaign.name, length: 16), @opportunity.campaign, title: @opportunity.campaign.name)
3
+ %dl
4
+ %li
5
+ %dt= @opportunity.stage ? t(@opportunity.stage) : t(:other)
6
+ %tt #{t :stage}:
7
+ %li
8
+ %dt= @opportunity.closes_on ? l(@opportunity.closes_on, format: :mmdd) : t(:n_a)
9
+ %tt #{t :close_date}:
10
+ - if @opportunity.closes_on && @opportunity.stage !~ /lost|won/
11
+ %li
12
+ - days_left = (@opportunity.closes_on - Date.today).to_i
13
+ - if days_left >= 0
14
+ %dt= days_left
15
+ %tt #{t :days_left}:
16
+ - else
17
+ %dt.warn= t('pluralize.day', count: (days_left * -1))
18
+ %tt #{t :days_late}:
19
+ %li
20
+ %dt= @opportunity.probability ? @opportunity.probability.to_s + "%" : t(:n_a)
21
+ %tt #{t :probability}:
22
+ %li
23
+ %dt= @opportunity.amount ? number_to_currency(@opportunity.amount.to_f, precision: 0) : t(:n_a)
24
+ %tt #{t :amount}:
25
+ %li
26
+ %dt= @opportunity.discount ? number_to_currency(@opportunity.discount.to_f, precision: 0) : t(:n_a)
27
+ - if @opportunity.amount && @opportunity.discount
28
+ %tt== Discount (#{number_to_percentage(@opportunity.discount / @opportunity.amount * 100, precision: 1)}):
47
29
  - else
48
- #{t :n_a}
49
- %tt #{t :campaign}:
30
+ %tt #{t :discount}:
31
+ %li
32
+ %dt= @opportunity.amount ? number_to_currency(@opportunity.weighted_amount.to_f, precision: 0) : t(:n_a)
33
+ %tt #{t :weighted_amount}:
34
+ %li
35
+ %dt= @opportunity.assigned_to ? truncate(@opportunity.assignee.full_name, length: 16) : nil
36
+ %tt #{t :assigned_to}:
37
+ %li
38
+ %dt
39
+ - if @opportunity.account
40
+ = link_to(truncate(@opportunity.account.name, length: 16), @opportunity.account, title: @opportunity.account.name)
41
+ - else
42
+ #{t :n_a}
43
+ %tt #{t :account}:
44
+ %li.last
45
+ %dt
46
+ - if @opportunity.campaign
47
+ = link_to(truncate(@opportunity.campaign.name, length: 16), @opportunity.campaign, title: @opportunity.campaign.name)
48
+ - else
49
+ #{t :n_a}
50
+ %tt #{t :campaign}:
50
51
 
51
52
  - unless @opportunity.background_info.blank?
52
53
  .caption #{t :background_info}
@@ -10,14 +10,14 @@
10
10
  $('##{dom_id(@entity)}').effect("highlight", { duration:1500 });
11
11
 
12
12
  - if called_from_index_page?
13
- = refresh_sidebar(:index, :filters)
13
+ = refresh_sidebar(:index)
14
14
  $('#paginate').html('#{ j render(partial: "shared/paginate_with_per_page") }');
15
15
  - elsif called_from_landing_page?("accounts") # Opportunity created on Account landing page: refresh Account summary.
16
16
  - @account.reload
17
- = refresh_sidebar_for(:accounts, :show, :summary)
17
+ = refresh_sidebar_for(:accounts, :show)
18
18
  - elsif called_from_landing_page?("campaigns") # Opportunity created on Campaign landing page: refresh Campaign summary.
19
19
  - @campaign.reload
20
- = refresh_sidebar_for(:campaigns, :show, :summary)
20
+ = refresh_sidebar_for(:campaigns, :show)
21
21
  - else # Opportunity created on Contact landing page: refresh recent items list.
22
22
  $('#recently').replaceWith('#{ j render(partial: "shared/recently") }');
23
23
  crm.flick('empty', 'remove');
@@ -25,5 +25,4 @@
25
25
  - else
26
26
  $('##{create_id}').html('#{ j render(partial: "new") }');
27
27
  crm.create_or_select_account(#{ request.referer =~ /\/accounts\// || @account.id.blank? });
28
- $('##{create_id}').effect("shake", { duration:250, distance: 6 });
29
28
  $('#new_#{entity_name} input[type!=hidden]').first().focus();
@@ -3,11 +3,11 @@
3
3
 
4
4
  $('##{dom_id(@entity)}').css('background-color', '#ffe4e1').slideUp(250);
5
5
  - if called_from_index_page?
6
- = refresh_sidebar(:index, :filters)
6
+ = refresh_sidebar(:index)
7
7
  $('#paginate').html('#{ j render(partial: "shared/paginate_with_per_page") }');
8
8
  - elsif called_from_landing_page?("accounts")
9
- = refresh_sidebar_for(:accounts, :show, :summary)
9
+ = refresh_sidebar_for(:accounts, :show)
10
10
  - elsif called_from_landing_page?("campaigns")
11
- = refresh_sidebar_for(:campaigns, :show, :summary)
11
+ = refresh_sidebar_for(:campaigns, :show)
12
12
  - else
13
13
  $('#recently').replaceWith('#{ j render(partial: "shared/recently") }');
@@ -7,6 +7,7 @@
7
7
  = styles_for :task, :contact
8
8
 
9
9
  = render 'opportunities/title_bar', opportunity: @opportunity
10
+ = render 'opportunities/sidebar_show', opportunity: @opportunity
10
11
 
11
12
  = render "comments/new", commentable: @opportunity
12
13
  = render partial: "shared/timeline", collection: @timeline
@@ -6,22 +6,21 @@
6
6
  - if called_from_landing_page?
7
7
  crm.flip_form('edit_#{entity_name}');
8
8
  crm.set_title('edit_#{entity_name}', '#{h @entity.name}');
9
- = refresh_sidebar(:show, :summary)
9
+ = refresh_sidebar(:show)
10
10
  - else
11
11
  $('##{id}').replaceWith('#{ j render(partial: entity_name, collection: [ @entity ]) }');
12
12
  $('##{id}').effect("highlight", { duration:1500 });
13
13
  - if called_from_index_page?
14
- = refresh_sidebar(:index, :filters)
14
+ = refresh_sidebar(:index)
15
15
  - elsif called_from_landing_page?(:accounts)
16
- = refresh_sidebar_for(:accounts, :show, :summary)
16
+ = refresh_sidebar_for(:accounts, :show)
17
17
  - elsif called_from_landing_page?(:campaigns)
18
- = refresh_sidebar_for(:campaigns, :show, :summary)
18
+ = refresh_sidebar_for(:campaigns, :show)
19
19
  - else
20
20
  $('#recently').replaceWith('#{ j render(partial: "shared/recently") }');
21
21
  - else
22
22
  $('##{id}').html('#{ j render(partial: "edit") }');
23
23
  crm.create_or_select_account(#{ request.referer =~ /\/accounts\// || @account.id.blank? });
24
- $('##{id}').effect("shake", { duration:250, distance: 6 });
25
24
  $('##{dom_id(@entity, :edit)} input[type!=hidden]').first().focus();
26
25
 
27
- = hook(:entity_update, self, {entity: @entity})
26
+ = hook(:entity_update, self, {entity: @entity})
@@ -1,31 +1,5 @@
1
1
  -# Generate dynamic styles that get embedded on the page.
2
2
  - content_for :styles do
3
3
 
4
- - if models.include?(:account)
5
- - Setting.account_category_color.each do |key, value|
6
- li.account .#{key} { background:#{value}; }
7
-
8
- - if models.include?(:campaign)
9
- - Setting.campaign_status_color.each do |key, value|
10
- li.campaign .#{key} { background:#{value}; }
11
-
12
- - if models.include?(:lead)
13
- - Setting.lead_status_color.each do |key, value|
14
- li.lead .#{key} { background:#{value}; }
15
-
16
- - if models.include?(:opportunity)
17
- - Setting.opportunity_stage_color.each do |key, value|
18
- li.opportunity .#{key} { background:#{value}; }
19
-
20
- - if models.include?(:task)
21
- - Setting.task_category_color.each do |key, value|
22
- li.task .#{key} { background:#{value}; }
23
-
24
- - if models.include?(:user)
25
- li.user .admin { background: lightblue; }
26
- li.user .suspended { background: gainsboro; }
27
- li.user .active { background: lightgreen; }
28
- li.user .signed_up { background: lightsalmon; }
29
-
30
4
  = hook(:inline_styles, self, local_assigns)
31
5
 
@@ -2,7 +2,7 @@
2
2
  - unless assigned.category.blank?
3
3
  .strip{class: assigned.category}= t(assigned.category)
4
4
  - else
5
- .strip.white #{t :other}
5
+ .strip.other #{t :other}
6
6
 
7
7
  .tools
8
8
  = link_to_task_edit(assigned, bucket) + " | "
@@ -2,7 +2,7 @@
2
2
  - unless completed.category.blank?
3
3
  .strip{class: completed.category}= t(completed.category)
4
4
  - else
5
- .strip.white #{t :other}
5
+ .strip.other #{t :other}
6
6
 
7
7
  .tools
8
8
  - if shown_on_landing_page?
@@ -9,7 +9,7 @@
9
9
  = render "fields/groups", f: f, edit: true
10
10
 
11
11
  .buttonbar
12
- = f.submit t :save_task
12
+ = f.submit t :save_task, class: 'btn btn-primary'
13
13
  == #{t :or}
14
14
  = link_to_cancel edit_task_path(@task)
15
15
  == #{t :or} #{t :move_to}
@@ -9,6 +9,6 @@
9
9
  = render "fields/groups", f: f
10
10
 
11
11
  .buttonbar
12
- = f.submit t :create_task
12
+ = f.submit t(:create_task), class: 'btn btn-primary'
13
13
  #{t :or}
14
14
  = link_to_cancel new_task_path + "?view=#{@view}"
@@ -2,7 +2,7 @@
2
2
  - unless pending.category.blank?
3
3
  .strip{class: pending.category}= t(pending.category)
4
4
  - else
5
- .strip.white #{t :other}
5
+ .strip.other #{t :other}
6
6
 
7
7
  .tools
8
8
  = link_to_task_edit(pending, bucket) + " | "
@@ -2,7 +2,7 @@
2
2
  - unless related.category.blank?
3
3
  .strip{class: related.category}= t(related.category)
4
4
  - else
5
- .strip{style: "color:white"} #{t :other}
5
+ .strip.other #{t :other}
6
6
  %div
7
7
  - if related.assigned_to
8
8
  = link_to(related.assignee.full_name, user_path(related.assignee))
@@ -1,8 +1,7 @@
1
- %table{ cellpadding: 3, cellspacing: 0, style: "width: 100%; margin: 2px 0px 10px 0px" }
2
- %tr
3
- %td{ class: "selector left " + (@view == "pending" ? "on" : "off"), width: "33%", onclick: %Q(document.location.href='#{tasks_path}') }
4
- = t :pending_tab
5
- %td{ class: "selector " + (@view == "assigned" ? "on" : "off"), width: "33%", onclick: %Q(document.location.href='#{url_for(action: :index, view: "assigned")}') }
6
- = t :assigned_tab
7
- %td{ class: "selector right " + (@view == "completed" ? "on" : "off"), width: "33%", onclick: %Q(document.location.href='#{url_for(action: :index, view: "completed")}') }
8
- = t :completed_tab
1
+ %nav{ class: "nav nav-pills nav-sm" }
2
+ %a{ class: "nav-link " + (@view == "pending" ? "active" : "off"), href: tasks_path }
3
+ = t :pending_tab
4
+ %a{ class: "nav-link " + (@view == "assigned" ? "active" : "off"), href: url_for(action: :index, view: "assigned") }
5
+ = t :assigned_tab
6
+ %a{ class: "nav-link " + (@view == "completed" ? "active" : "off"), href: url_for(action: :index, view: "completed") }
7
+ = t :completed_tab
@@ -2,13 +2,13 @@
2
2
  = render "selector"
3
3
  - if @view == "pending" || @view == "assigned"
4
4
  - Setting.unroll(:task_bucket).each do |value, key|
5
- .check_box{style: "border-bottom: 1px silver dotted;"}
5
+ .check_box
6
6
  %div{style: "float:right;"}
7
7
  = @task_total[key]
8
8
  = task_filter_checkbox(@view, key, @task_total[key]) + " " + t(value)
9
9
  - else # @view == "completed"
10
10
  - Setting.unroll(:task_completed).each do |value, key|
11
- .check_box{style: "border-bottom: 1px silver dotted;"}
11
+ .check_box
12
12
  %div{style: "float:right;"}
13
13
  = @task_total[key]
14
14
  = task_filter_checkbox(@view, key, @task_total[key]) + " " + t(value)
@@ -1,5 +1,5 @@
1
1
  .title_tools
2
- = link_to_inline(:create_task, url_for(action: :new, view: @view), text: t(:create_task))
2
+ = link_to_inline(:create_task, url_for(action: :new, view: @view), text: t(:create_task), class: 'btn-sm btn-primary')
3
3
  .title#title
4
4
  = (@view == "assigned" ? t(:assigned_tasks) : t(:tasks))
5
5
  = image_tag("loading.gif", size: :thumb, id: "loading", style: "display: none;")
@@ -4,7 +4,7 @@
4
4
  $('##{dom_id(@task)}').fadeOut({ duration:500 });
5
5
  - if @empty_bucket
6
6
  $('#list_#{@empty_bucket}').fadeOut({ duration:500 });
7
- = refresh_sidebar(:index, :filters)
7
+ = refresh_sidebar(:index)
8
8
 
9
9
  - else # Completing from the Asset page -- replace task partial with completed one.
10
10
 
@@ -22,7 +22,7 @@
22
22
  $('#list_#{bucket}').show();
23
23
  $('##{bucket}').before('#{ j render(partial: @view, collection: [ @task ], locals: { bucket: bucket }) }');
24
24
  $('##{dom_id(@task)}').effect("highlight", { duration:1500 });
25
- = refresh_sidebar(:index, :filters)
25
+ = refresh_sidebar(:index)
26
26
 
27
27
  crm.flick('empty', 'remove');
28
28
 
@@ -34,6 +34,5 @@
34
34
  $('#recently').replaceWith('#{ j render(partial: "shared/recently") }');
35
35
 
36
36
  - else # Couldn't create the task -- validation failed.
37
- $('#create_task').effect("shake", { duration:250, distance: 6 });
38
37
  $('#task_name').focus();
39
38
  $('#new_task input[type=submit]').enable();
@@ -4,4 +4,4 @@ $('##{dom_id(@task)}').slideUp(250);
4
4
  $('#list_#{@empty_bucket}').fadeOut({ duration:500 });
5
5
 
6
6
  - if called_from_index_page?
7
- = refresh_sidebar(:index, :filters)
7
+ = refresh_sidebar(:index)
@@ -5,8 +5,7 @@
5
5
  $('#list_#{@empty_bucket}').fadeOut({ duration:500 });
6
6
 
7
7
  - if called_from_index_page?
8
- = refresh_sidebar(:index, :filters)
8
+ = refresh_sidebar(:index)
9
9
  - else # Errors
10
- $('##{dom_id(@task)}').effect("shake", { duration:250, distance: 6 });
11
10
  $('#task_name').focus();
12
11
  $('#task_submit').enable();
@@ -14,6 +14,5 @@
14
14
  = replace_content(@task, @task.bucket)
15
15
 
16
16
  - else # Errors
17
- $('##{dom_id(@task)}').effect("shake", { duration:250, distance: 6 });
18
17
  $('#task_name').focus();
19
18
  $('#task_submit').enable();
@@ -6,7 +6,6 @@
6
6
  - flash[:notice] = nil
7
7
  - else
8
8
  $('#change_password').html('#{ j render(partial: "password") }');
9
- $('#change_password').effect("shake", { duration:250, distance: 6 });
10
9
  - if @user.errors[:current_password].present?
11
10
  $('#current_password').focus();
12
11
  - else
@@ -7,5 +7,4 @@
7
7
  $('#profile').html('#{ j render(partial: "user") }');
8
8
  - else
9
9
  $('#edit_profile').html('#{ j render(partial: "profile") }');
10
- $('#edit_profile').effect("shake", { duration:250, distance: 6 });
11
10
  $('#user_email').focus();
@@ -4,4 +4,3 @@
4
4
  $('#profile').html('#{ j render(partial: "user") }');
5
5
  - else
6
6
  $('#upload_avatar').html('#{ j render(partial: "avatar") }');
7
- $('#upload_avatar').effect("shake", { duration:250, distance: 6 });
@@ -1,6 +1,6 @@
1
1
  - if [first, second].any?(&:present?)
2
2
  %br
3
- - if first.blank? and second.present?
3
+ - if first.blank? and second.present? && !second.is_a?(Time)
4
4
  = t('version.set_html', attr: label, to: second)
5
5
  - elsif first.present? and second.present?
6
6
  = t('version.change_html', attr: label, from: first, to: second)
data/bin/setup CHANGED
@@ -2,7 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'fileutils'
5
- include FileUtils
6
5
 
7
6
  # path to your application root.
8
7
  APP_ROOT = File.expand_path('..', __dir__)
@@ -11,24 +10,25 @@ def system!(*args)
11
10
  system(*args) || abort("\n== Command #{args} failed ==")
12
11
  end
13
12
 
14
- chdir APP_ROOT do
15
- # This script is a starting point to setup your application.
13
+ FileUtils.chdir APP_ROOT do
14
+ # This script is a way to setup or update your development environment automatically.
15
+ # This script is idempotent, so that you can run it at anytime and get an expectable outcome.
16
16
  # Add necessary setup steps to this file.
17
17
 
18
18
  puts '== Installing dependencies =='
19
19
  system! 'gem install bundler --conservative'
20
20
  system('bundle check') || system!('bundle install')
21
21
 
22
- # Install JavaScript dependencies if using Yarn
22
+ # Install JavaScript dependencies
23
23
  # system('bin/yarn')
24
24
 
25
25
  # puts "\n== Copying sample files =="
26
26
  # unless File.exist?('config/database.yml')
27
- # cp 'config/database.yml.sample', 'config/database.yml'
27
+ # FileUtils.cp 'config/database.yml.sample', 'config/database.yml'
28
28
  # end
29
29
 
30
30
  puts "\n== Preparing database =="
31
- system! 'bin/rails db:setup'
31
+ system! 'bin/rails db:prepare'
32
32
 
33
33
  puts "\n== Removing old logs and tempfiles =="
34
34
  system! 'bin/rails log:clear tmp:clear'
@@ -29,7 +29,7 @@ require 'fat_free_crm/gem_ext/rails/engine'
29
29
  module FatFreeCRM
30
30
  class Application < Rails::Application
31
31
  # Initialize configuration defaults for originally generated Rails version.
32
- config.load_defaults 5.0
32
+ config.load_defaults 6.0
33
33
 
34
34
  # Settings in config/environments/* take precedence over those specified here.
35
35
  # Application configuration should go into files in config/initializers
@@ -9,4 +9,4 @@
9
9
  require_relative 'application'
10
10
 
11
11
  # Initialize the rails application
12
- FatFreeCRM::Application.initialize!
12
+ Rails.application.initialize!
@@ -12,6 +12,8 @@
12
12
  # policy.object_src :none
13
13
  # policy.script_src :self, :https
14
14
  # policy.style_src :self, :https
15
+ # # If you are using webpack-dev-server then specify webpack-dev-server host
16
+ # policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
15
17
 
16
18
  # # Specify URI for violation reports
17
19
  # # policy.report_uri "/csp-violation-report-endpoint"
@@ -20,6 +22,9 @@
20
22
  # If you are using UJS then enable automatic nonce generation
21
23
  # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
22
24
 
25
+ # Set the nonce only to specific directives
26
+ # Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
27
+
23
28
  # Report CSP violations to a specified URI
24
29
  # For further information see the following documentation:
25
30
  # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only