fat_free_crm 0.24.3 → 0.25.0
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.
Potentially problematic release.
This version of fat_free_crm might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +345 -7
- data/README.md +22 -19
- data/app/assets/stylesheets/advanced_search.css.scss +1 -1
- data/app/assets/stylesheets/base.scss +11 -7
- data/app/assets/stylesheets/common.scss +20 -17
- data/app/assets/stylesheets/header.scss +2 -2
- data/app/assets/stylesheets/rails.scss +2 -2
- data/app/controllers/comments_controller.rb +1 -1
- data/app/controllers/entities/contacts_controller.rb +3 -1
- data/app/controllers/entities/leads_controller.rb +3 -1
- data/app/controllers/home_controller.rb +1 -1
- data/app/controllers/users_controller.rb +1 -1
- data/app/helpers/application_helper.rb +42 -4
- data/app/helpers/contacts_helper.rb +28 -0
- data/app/mailers/subscription_mailer.rb +1 -1
- data/app/models/entities/account.rb +2 -0
- data/app/models/observers/opportunity_observer.rb +1 -1
- data/app/views/accounts/_contact_info.html.haml +14 -6
- data/app/views/accounts/_index_long.html.haml +1 -1
- data/app/views/accounts/_sidebar_show.html.haml +23 -5
- data/app/views/admin/users/_user.html.haml +6 -4
- data/app/views/comments/_comment.html.haml +3 -3
- data/app/views/contacts/_extra.html.haml +3 -3
- data/app/views/contacts/_index_full.html.haml +2 -2
- data/app/views/contacts/_index_long.html.haml +2 -2
- data/app/views/contacts/_section_general.html.haml +1 -1
- data/app/views/contacts/_sidebar_show.html.haml +3 -3
- data/app/views/contacts/_top_section.html.haml +2 -2
- data/app/views/contacts/_web.html.haml +1 -1
- data/app/views/home/_account.html.haml +1 -1
- data/app/views/layouts/application.html.haml +4 -0
- data/app/views/leads/_contact.html.haml +3 -3
- data/app/views/leads/_index_long.html.haml +2 -2
- data/app/views/leads/_opportunity.html.haml +2 -2
- data/app/views/leads/_sidebar_show.html.haml +2 -2
- data/app/views/leads/_status.html.haml +1 -1
- data/app/views/leads/_top_section.html.haml +1 -1
- data/app/views/opportunities/_top_section.html.haml +2 -2
- data/app/views/tasks/create.js.haml +1 -1
- data/app/views/users/_profile.html.haml +2 -2
- data/app/views/users/_user.html.haml +6 -5
- data/config/initializers/simple_form.rb +1 -1
- data/config/locales/fat_free_crm.cs.yml +3 -3
- data/config/locales/fat_free_crm.de.yml +3 -3
- data/config/locales/fat_free_crm.en-GB.yml +3 -3
- data/config/locales/fat_free_crm.en-US.yml +3 -3
- data/config/locales/fat_free_crm.es-CL.yml +3 -3
- data/config/locales/fat_free_crm.es.yml +3 -3
- data/config/locales/fat_free_crm.et.yml +3 -3
- data/config/locales/fat_free_crm.fr-CA.yml +3 -3
- data/config/locales/fat_free_crm.fr.yml +3 -3
- data/config/locales/fat_free_crm.it.yml +3 -3
- data/config/locales/fat_free_crm.ja.yml +3 -3
- data/config/locales/fat_free_crm.nl.yml +3 -3
- data/config/locales/fat_free_crm.pl.yml +3 -3
- data/config/locales/fat_free_crm.pt-BR.yml +3 -3
- data/config/locales/fat_free_crm.ru.yml +3 -3
- data/config/locales/fat_free_crm.sv-SE.yml +3 -3
- data/config/locales/fat_free_crm.th.yml +3 -3
- data/config/locales/fat_free_crm.zh-CN.yml +3 -3
- data/config/settings.default.yml +10 -2
- data/db/demo/accounts.yml +2 -0
- data/db/fat_free_crm_development.sqlite3 +0 -0
- data/db/{fat_free_crm_test.sqlite3-shm → fat_free_crm_development.sqlite3-shm} +0 -0
- data/db/fat_free_crm_development.sqlite3-wal +0 -0
- data/db/fat_free_crm_test.sqlite3 +0 -0
- data/db/migrate/20100928030623_create_addresses.rb +1 -1
- data/db/migrate/20250805093408_add_latitude_and_longitude_to_accounts.rb +8 -0
- data/db/schema.rb +12 -1
- data/lib/fat_free_crm/fields.rb +3 -0
- data/lib/fat_free_crm/mail_processor/dropbox.rb +1 -1
- data/lib/fat_free_crm/version.rb +2 -2
- metadata +10 -9
- data/db/fat_free_crm_test.sqlite3-wal +0 -0
@@ -87,11 +87,11 @@ $sidebar_width: 210px;
|
|
87
87
|
-moz-border-radius: 6px;
|
88
88
|
-webkit-border-radius: 6px;
|
89
89
|
input[type="text"], input[type="email"], input[type="password"] {
|
90
|
-
font-size:
|
90
|
+
font-size: 1.2em;
|
91
91
|
padding: 2px;
|
92
92
|
width: 355px; }
|
93
93
|
input[type="submit"] {
|
94
|
-
font-size:
|
94
|
+
font-size: 1.1em; } }
|
95
95
|
|
96
96
|
.tabbed {
|
97
97
|
padding: 12px 12px 12px 16px; }
|
@@ -304,7 +304,6 @@ $bg_color9: #FFDD4A;
|
|
304
304
|
.amount {
|
305
305
|
background: palegreen;
|
306
306
|
color: black;
|
307
|
-
font-size: 11px;
|
308
307
|
text-align: center;
|
309
308
|
padding: 0px 3px 0px 3px;
|
310
309
|
-moz-border-radius: {
|
@@ -322,7 +321,7 @@ $bg_color9: #FFDD4A;
|
|
322
321
|
background: gainsboro;
|
323
322
|
color: black;
|
324
323
|
float: left;
|
325
|
-
font-size:
|
324
|
+
font-size: 0.85em;
|
326
325
|
margin: 0px 6px 0px 0px;
|
327
326
|
padding: 1px 4px 1px 3px;
|
328
327
|
text-align: right;
|
@@ -351,8 +350,7 @@ $bg_color9: #FFDD4A;
|
|
351
350
|
margin: 0px 0px 10px 0px; }
|
352
351
|
|
353
352
|
.prefix {
|
354
|
-
width: 30px;
|
355
|
-
font-size: 10px; }
|
353
|
+
width: 30px; }
|
356
354
|
|
357
355
|
.comment {
|
358
356
|
background: #f2f2f2;
|
@@ -372,7 +370,7 @@ $bg_color9: #FFDD4A;
|
|
372
370
|
input[type=text] {
|
373
371
|
border: 1px #cfcfcf solid;
|
374
372
|
color: grey;
|
375
|
-
font-size:
|
373
|
+
font-size: 0.9em;
|
376
374
|
margin: 3px 0px 0px 0px;
|
377
375
|
padding: 3px;
|
378
376
|
width: 500px; }
|
@@ -381,9 +379,8 @@ $bg_color9: #FFDD4A;
|
|
381
379
|
margin: 3px 10px 3px 3px; }
|
382
380
|
.body {
|
383
381
|
display: inline;
|
384
|
-
font-size:
|
382
|
+
font-size: 0.9em; }
|
385
383
|
tt {
|
386
|
-
font-size: 10px;
|
387
384
|
color: #666666; }
|
388
385
|
dt {
|
389
386
|
padding: 0px;
|
@@ -394,10 +391,10 @@ $bg_color9: #FFDD4A;
|
|
394
391
|
margin-top: 7px;
|
395
392
|
margin-left: 4px;
|
396
393
|
a {
|
397
|
-
font-size:
|
394
|
+
font-size: 0.9em; } } }
|
398
395
|
|
399
396
|
.comment textarea, textarea#comment_body {
|
400
|
-
font-size:
|
397
|
+
font-size: 0.9em;
|
401
398
|
height: 110px;
|
402
399
|
margin: 3px 0px 0px 0px;
|
403
400
|
padding: 3px;
|
@@ -418,9 +415,8 @@ $bg_color9: #FFDD4A;
|
|
418
415
|
color: black; }
|
419
416
|
.body {
|
420
417
|
display: inline;
|
421
|
-
font-size:
|
418
|
+
font-size: 0.9em; }
|
422
419
|
tt {
|
423
|
-
font-size: 10px;
|
424
420
|
color: #666666; }
|
425
421
|
dt {
|
426
422
|
padding: 0px;
|
@@ -432,12 +428,12 @@ $bg_color9: #FFDD4A;
|
|
432
428
|
font-weight: normal; }
|
433
429
|
|
434
430
|
#export {
|
435
|
-
font-size:
|
431
|
+
font-size: 0.8em;
|
436
432
|
margin: 12px 0px 0px 0px; }
|
437
433
|
|
438
434
|
#paginate {
|
439
435
|
float: right;
|
440
|
-
font-size:
|
436
|
+
font-size: 0.9em;
|
441
437
|
margin: 4px 0px 0px 0px; }
|
442
438
|
|
443
439
|
#paging {
|
@@ -447,7 +443,7 @@ $bg_color9: #FFDD4A;
|
|
447
443
|
.confirm {
|
448
444
|
background: #cdfecd;
|
449
445
|
border-left: 10px palegreen solid;
|
450
|
-
font-size:
|
446
|
+
font-size: 0.9em;
|
451
447
|
margin: 0px 0px 6px 0px;
|
452
448
|
padding: 6px; }
|
453
449
|
|
@@ -544,7 +540,7 @@ $bg_color9: #FFDD4A;
|
|
544
540
|
margin: 4px 0px 4px 0px; } } }
|
545
541
|
|
546
542
|
.arrow {
|
547
|
-
font-size:
|
543
|
+
font-size: 0.8em;
|
548
544
|
padding: 0px 2px 3px 0px; }
|
549
545
|
|
550
546
|
.left {
|
@@ -785,6 +781,13 @@ table.asset_attributes {
|
|
785
781
|
}
|
786
782
|
}
|
787
783
|
|
784
|
+
.web-presence-icons a {
|
785
|
+
font-size: larger;
|
786
|
+
color: black;
|
787
|
+
:hover {
|
788
|
+
color: black;
|
789
|
+
}
|
790
|
+
}
|
788
791
|
|
789
792
|
// Login page
|
790
793
|
.form-group.user_remember_me {
|
@@ -29,7 +29,7 @@ $color_footer: grey;
|
|
29
29
|
text-decoration: none; }
|
30
30
|
#welcome {
|
31
31
|
float: right;
|
32
|
-
font-size:
|
32
|
+
font-size: 0.9em;
|
33
33
|
a {
|
34
34
|
color: lightyellow;
|
35
35
|
padding: 1px 4px 2px 4px;
|
@@ -108,6 +108,6 @@ $color_footer: grey;
|
|
108
108
|
|
109
109
|
#footer {
|
110
110
|
color: $color_footer;
|
111
|
-
font-size:
|
111
|
+
font-size: 0.8em;
|
112
112
|
padding: 0px 0px 0px 16px;
|
113
113
|
text-align: center; }
|
@@ -50,7 +50,7 @@ p, div {
|
|
50
50
|
&.flash_exception {
|
51
51
|
background: #ddff99;
|
52
52
|
border: 5px limegreen solid;
|
53
|
-
font-size:
|
53
|
+
font-size: 1.1em;
|
54
54
|
margin: 50px auto;
|
55
55
|
padding: 50px;
|
56
56
|
width: 450px;
|
@@ -94,7 +94,7 @@ div {
|
|
94
94
|
.pagination, .per_page_options {
|
95
95
|
background: white;
|
96
96
|
float: right;
|
97
|
-
font-size:
|
97
|
+
font-size: 0.9em;
|
98
98
|
margin: 6px 0px 0px 0px;
|
99
99
|
a, span, em {
|
100
100
|
padding: 0.2em 0.5em;
|
@@ -59,7 +59,7 @@ class CommentsController < ApplicationController
|
|
59
59
|
|
60
60
|
# PUT /comments/1
|
61
61
|
# PUT /comments/1.json
|
62
|
-
# PUT /comments/1.xml
|
62
|
+
# PUT /comments/1.xml not implemented
|
63
63
|
#----------------------------------------------------------------------------
|
64
64
|
def update
|
65
65
|
@comment = Comment.find(params[:id])
|
@@ -26,7 +26,9 @@ class ContactsController < EntitiesController
|
|
26
26
|
@stage = Setting.unroll(:opportunity_stage)
|
27
27
|
@comment = Comment.new
|
28
28
|
@timeline = timeline(@contact)
|
29
|
-
respond_with(@contact)
|
29
|
+
respond_with(@contact) do |format|
|
30
|
+
format.vcf { send_data helpers.vcard_for(@contact).to_s, filename: "#{@contact.full_name}.vcf", disposition: 'attachment', type: 'text/x-vcard' }
|
31
|
+
end
|
30
32
|
end
|
31
33
|
|
32
34
|
# GET /contacts/new
|
@@ -26,7 +26,9 @@ class LeadsController < EntitiesController
|
|
26
26
|
def show
|
27
27
|
@comment = Comment.new
|
28
28
|
@timeline = timeline(@lead)
|
29
|
-
respond_with(@lead)
|
29
|
+
respond_with(@lead) do |format|
|
30
|
+
format.vcf { send_data helpers.vcard_for(@lead).to_s, filename: "#{@lead.full_name}.vcf", disposition: 'attachment', type: 'text/x-vcard' }
|
31
|
+
end
|
30
32
|
end
|
31
33
|
|
32
34
|
# GET /leads/new
|
@@ -130,7 +130,7 @@ class HomeController < ApplicationController
|
|
130
130
|
|
131
131
|
#----------------------------------------------------------------------------
|
132
132
|
# TODO: this is ugly, ugly code. It's being security patched now but urgently
|
133
|
-
# needs refactoring to use user id instead.
|
133
|
+
# needs refactoring to use user id instead. Permutations based on name or email
|
134
134
|
# yield incorrect results.
|
135
135
|
def activity_user
|
136
136
|
return nil if current_user.pref[:activity_user] == "all_users"
|
@@ -6,7 +6,7 @@
|
|
6
6
|
# See MIT-LICENSE file or http://www.opensource.org/licenses/mit-license.php
|
7
7
|
#------------------------------------------------------------------------------
|
8
8
|
class UsersController < ApplicationController
|
9
|
-
before_action :set_current_tab, only: %i[show opportunities_overview] # Don't
|
9
|
+
before_action :set_current_tab, only: %i[show opportunities_overview] # Don't highlight any tabs.
|
10
10
|
|
11
11
|
check_authorization
|
12
12
|
|
@@ -170,6 +170,25 @@ module ApplicationHelper
|
|
170
170
|
end
|
171
171
|
end
|
172
172
|
|
173
|
+
# Format a phone number as a tel: hyperlink.
|
174
|
+
#----------------------------------------------------------------------------
|
175
|
+
def link_to_phone(number)
|
176
|
+
return nil if number.blank?
|
177
|
+
|
178
|
+
sanitized_number = number.gsub(/[^0-9+]/, '')
|
179
|
+
link_to number, "tel:#{sanitized_number}"
|
180
|
+
end
|
181
|
+
|
182
|
+
# Render a phone field with an optional pattern for international format.
|
183
|
+
#----------------------------------------------------------------------------
|
184
|
+
def phone_field_with_pattern(form, method, options = {})
|
185
|
+
if Setting.enforce_international_phone_format
|
186
|
+
options[:pattern] ||= '\+[0-9]{1,3}\s?[0-9]{1,14}'
|
187
|
+
options[:placeholder] ||= '+1 123 456 7890'
|
188
|
+
end
|
189
|
+
form.phone_field(method, options)
|
190
|
+
end
|
191
|
+
|
173
192
|
#----------------------------------------------------------------------------
|
174
193
|
def jumpbox(current)
|
175
194
|
tabs = %i[campaigns accounts leads contacts opportunities]
|
@@ -248,7 +267,15 @@ module ApplicationHelper
|
|
248
267
|
# Display web presence mini-icons for Contact or Lead.
|
249
268
|
#----------------------------------------------------------------------------
|
250
269
|
def web_presence_icons(person)
|
251
|
-
|
270
|
+
sites = []
|
271
|
+
icon_for_site = {
|
272
|
+
skype: "skype",
|
273
|
+
facebook: "facebook",
|
274
|
+
linkedin: "linkedin",
|
275
|
+
twitter: "twitter",
|
276
|
+
blog: "external-link"
|
277
|
+
}
|
278
|
+
%i[blog linkedin facebook twitter skype].each do |site|
|
252
279
|
url = person.send(site)
|
253
280
|
next if url.blank?
|
254
281
|
|
@@ -257,8 +284,19 @@ module ApplicationHelper
|
|
257
284
|
else
|
258
285
|
url = "http://" + url unless url.match?(%r{^https?://})
|
259
286
|
end
|
260
|
-
|
261
|
-
|
287
|
+
sites << if icon_for_site[site]
|
288
|
+
link_to(content_tag(:i, "", { class: "fa fa-#{icon_for_site[site]}" }), h(url), "data-popup": true, title: t(:open_in_window, h(url)))
|
289
|
+
else
|
290
|
+
link_to(image_tag("#{site}.gif", size: "15x15"), h(url), "data-popup": true, title: t(:open_in_window, h(url)))
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
if person.is_a?(Contact)
|
295
|
+
sites << link_to(content_tag(:i, "", { class: "fa fa-address-card" }), contact_path(person, format: :vcf), title: "VCard")
|
296
|
+
elsif person.is_a?(Lead)
|
297
|
+
sites << link_to(content_tag(:i, "", { class: "fa fa-address-card" }), lead_path(person, format: :vcf), title: "VCard")
|
298
|
+
end
|
299
|
+
content_tag(:span, class: "web-presence-icons") { safe_join(sites, "\n") }
|
262
300
|
end
|
263
301
|
|
264
302
|
# Ajax helper to refresh current index page once the user selects an option.
|
@@ -319,7 +357,7 @@ module ApplicationHelper
|
|
319
357
|
|
320
358
|
# Entities can have associated avatars or gravatars. Only calls Gravatar
|
321
359
|
# in production env. Gravatar won't serve default images if they are not
|
322
|
-
#
|
360
|
+
# public: https://en.gravatar.com/site/implement/images
|
323
361
|
#----------------------------------------------------------------------------
|
324
362
|
def avatar_for(model, args = {})
|
325
363
|
args = { class: 'gravatar', size: :large }.merge(args)
|
@@ -18,4 +18,32 @@ module ContactsHelper
|
|
18
18
|
summary << "#{t(:mobile_small)}: #{contact.mobile}" if contact.mobile.present?
|
19
19
|
summary.join(', ')
|
20
20
|
end
|
21
|
+
|
22
|
+
def vcard_for(contact)
|
23
|
+
card = VCardigan.create
|
24
|
+
card.name contact.last_name, contact.first_name
|
25
|
+
card.fullname "#{contact.first_name} #{contact.last_name}"
|
26
|
+
card.title contact.title if contact.title.present?
|
27
|
+
if contact.respond_to?(:account) # Contact
|
28
|
+
card.org contact.account.name, contact.department if contact.account.present?
|
29
|
+
elsif contact.respond_to?(:company) # Lead
|
30
|
+
card.org contact.company if contact.company.present?
|
31
|
+
end
|
32
|
+
card.email contact.email, type: %w[internet work] if contact.email.present?
|
33
|
+
card.email contact.alt_email, type: %w[internet work] if contact.alt_email.present?
|
34
|
+
card.tel contact.phone, type: 'work' if contact.phone?
|
35
|
+
card.tel contact.mobile, type: %w[cell voice] if contact.mobile.present?
|
36
|
+
card.note "Exported from Fat Free CRM"
|
37
|
+
|
38
|
+
if contact.business_address
|
39
|
+
card.adr contact.business_address.street1,
|
40
|
+
contact.business_address.street2,
|
41
|
+
contact.business_address.city,
|
42
|
+
contact.business_address.state,
|
43
|
+
contact.business_address.zipcode,
|
44
|
+
contact.business_address.country, type: 'work'
|
45
|
+
end
|
46
|
+
|
47
|
+
card
|
48
|
+
end
|
21
49
|
end
|
@@ -14,7 +14,7 @@ class SubscriptionMailer < ActionMailer::Base
|
|
14
14
|
@comment = comment
|
15
15
|
@user = comment.user
|
16
16
|
|
17
|
-
# If entity has tags, join them and wrap in
|
17
|
+
# If entity has tags, join them and wrap in parentheses
|
18
18
|
subject = "RE: [#{@entity_type.downcase}:#{@entity.id}] #{@entity_name}"
|
19
19
|
subject += " (#{@entity.tags.join(', ')})" if @entity.tags.any?
|
20
20
|
|
@@ -77,6 +77,8 @@ class Account < ActiveRecord::Base
|
|
77
77
|
validates_uniqueness_of :name, scope: :deleted_at, if: -> { Setting.require_unique_account_names }
|
78
78
|
validates :rating, inclusion: { in: 0..5 }, allow_blank: true
|
79
79
|
validates :category, inclusion: { in: proc { Setting.unroll(:account_category).map { |s| s.last.to_s } } }, allow_blank: true
|
80
|
+
validates :latitude, numericality: { greater_than_or_equal_to: -90, less_than_or_equal_to: 90, allow_blank: true }
|
81
|
+
validates :longitude, numericality: { greater_than_or_equal_to: -180, less_than_or_equal_to: 180, allow_blank: true }
|
80
82
|
validate :users_for_shared_access
|
81
83
|
|
82
84
|
before_save :nullify_blank_category
|
@@ -25,7 +25,7 @@ class OpportunityObserver < ActiveRecord::Observer
|
|
25
25
|
update_campaign_revenue(item.campaign, item.amount.to_f - item.discount.to_f)
|
26
26
|
item.update_attribute(:probability, 100) # Set probability to 100% if won
|
27
27
|
log_activity(item, :won)
|
28
|
-
elsif original.stage == "won" && item.stage != "won" # :won to :other --
|
28
|
+
elsif original.stage == "won" && item.stage != "won" # :won to :other -- subtract from total campaign revenue.
|
29
29
|
update_campaign_revenue(original.campaign, -(original.amount.to_f - original.discount.to_f))
|
30
30
|
elsif original.stage != "lost" && item.stage == "lost"
|
31
31
|
item.update_attribute(:probability, 0) # Set probability to 0% if lost
|
@@ -1,5 +1,5 @@
|
|
1
1
|
- edit ||= false
|
2
|
-
- collapsed =
|
2
|
+
- collapsed = false
|
3
3
|
= subtitle :account_contact, collapsed, t(:contact_info)
|
4
4
|
.section
|
5
5
|
%small#account_contact_intro{ hidden_if(!collapsed) }
|
@@ -12,23 +12,31 @@
|
|
12
12
|
%tr
|
13
13
|
%td
|
14
14
|
.label #{t :phone_toll_free}:
|
15
|
-
= f
|
15
|
+
= phone_field_with_pattern(f, :toll_free_phone, style: "width:154px")
|
16
16
|
%td= spacer
|
17
17
|
%td
|
18
18
|
.label #{t :phone}:
|
19
|
-
= f
|
19
|
+
= phone_field_with_pattern(f, :phone, style: "width:154px")
|
20
20
|
%td= spacer
|
21
21
|
%td
|
22
22
|
.label #{t :fax}:
|
23
|
-
= f
|
23
|
+
= phone_field_with_pattern(f, :fax, style: "width:154px")
|
24
24
|
%tr
|
25
25
|
%td
|
26
26
|
.label.top #{t :website}:
|
27
|
-
= f.
|
27
|
+
= f.url_field :website
|
28
28
|
%td= spacer
|
29
29
|
%td
|
30
30
|
.label.top Email:
|
31
|
-
= f.
|
31
|
+
= f.email_field :email
|
32
|
+
%tr
|
33
|
+
%td
|
34
|
+
.label.top Latitude:
|
35
|
+
= f.text_field :latitude, pattern: "^-?\\d+(\\.\\d+)?$"
|
36
|
+
%td= spacer
|
37
|
+
%td
|
38
|
+
.label.top Longitude:
|
39
|
+
= f.text_field :longitude, pattern: "^-?\\d+(\\.\\d+)?$"
|
32
40
|
%tr
|
33
41
|
%td
|
34
42
|
= render "shared/address", f: f, asset: @account, type: :billing, title: :billing_address
|
@@ -33,7 +33,7 @@
|
|
33
33
|
= stars_for(account)
|
34
34
|
= " | ".html_safe << link_to(account.website, account.website.to_url) if account.website.present?
|
35
35
|
= " | ".html_safe << link_to_email(account.email) if account.email.present?
|
36
|
-
= " | ".html_safe << t(:phone_small) << ": " << (account.toll_free_phone? ? account.toll_free_phone : account.phone) if account.toll_free_phone? || account.phone?
|
36
|
+
= " | ".html_safe << t(:phone_small) << ": " << (account.toll_free_phone? ? link_to_phone(account.toll_free_phone) : link_to_phone(account.phone)) if account.toll_free_phone? || account.phone?
|
37
37
|
|
38
38
|
- if account.tags.present?
|
39
39
|
%dt
|
@@ -12,16 +12,26 @@
|
|
12
12
|
|
13
13
|
%div
|
14
14
|
- if @account.toll_free_phone
|
15
|
-
#{t :phone_toll_free}: <b>#{@account.toll_free_phone}</b><br />
|
15
|
+
== #{t :phone_toll_free}: <b>#{link_to_phone(@account.toll_free_phone)}</b><br />
|
16
16
|
|
17
17
|
- if @account.phone
|
18
|
-
#{t :phone}: <b>#{@account.phone}</b><br />
|
18
|
+
== #{t :phone}: <b>#{link_to_phone(@account.phone)}</b><br />
|
19
19
|
|
20
20
|
- if @account.fax
|
21
|
-
#{t :fax}: <b>#{@account.fax}</b><br />
|
21
|
+
== #{t :fax}: <b>#{link_to_phone(@account.fax)}</b><br />
|
22
22
|
|
23
|
-
|
24
|
-
|
23
|
+
%div= render "shared/address_show", asset: @account, type: 'billing', title: :billing_address
|
24
|
+
%div= render "shared/address_show", asset: @account, type: 'shipping', title: :shipping_address
|
25
|
+
|
26
|
+
|
27
|
+
- if @account.latitude.present? && @account.longitude.present?
|
28
|
+
#map{style: "width: 100%; height: 150px;"}
|
29
|
+
|
30
|
+
%p.text-end
|
31
|
+
%small
|
32
|
+
%b= link_to "Google Maps", "https://www.google.com/maps/search/?api=1&query=#{@account.latitude},#{@account.longitude}", target: "_blank"
|
33
|
+
|
|
34
|
+
%b= link_to "Other", "geo:#{@account.latitude},#{@account.longitude}"
|
25
35
|
|
26
36
|
.caption #{t :account_summary}
|
27
37
|
%dl
|
@@ -55,3 +65,11 @@
|
|
55
65
|
.tags= tags_for_index(@account)
|
56
66
|
|
57
67
|
= hook(:show_account_sidebar_bottom, self, account: @account)
|
68
|
+
|
69
|
+
- if @account.latitude.present? && @account.longitude.present?
|
70
|
+
:javascript
|
71
|
+
var map = L.map('map').setView([#{@account.latitude}, #{@account.longitude}], 18);
|
72
|
+
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
73
|
+
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
74
|
+
}).addTo(map);
|
75
|
+
L.marker([#{@account.latitude}, #{@account.longitude}]).addTo(map);
|
@@ -47,11 +47,13 @@
|
|
47
47
|
%dt{ style: "padding: 2px 0px 0px 0px" }
|
48
48
|
= link_to_email(user.email.to_s) + " | "
|
49
49
|
- if user.phone?
|
50
|
-
= t(:phone_small) + ":"
|
51
|
-
=
|
50
|
+
= (t(:phone_small) + ": ").html_safe
|
51
|
+
%b= link_to_phone(user.phone)
|
52
|
+
= " | ".html_safe
|
52
53
|
- if user.mobile?
|
53
|
-
= t(:mobile_small) + ":"
|
54
|
-
=
|
54
|
+
= (t(:mobile_small) + ": ").html_safe
|
55
|
+
%b= link_to_phone(user.mobile)
|
56
|
+
= " | ".html_safe
|
55
57
|
|
56
58
|
- if !user.suspended?
|
57
59
|
%span #{t(:user_since, l(user.created_at.to_date, format: :mmddyy))}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
- truncated = simple_format(truncate(comment.comment.gsub("\n", " "), length: 125))
|
2
2
|
- formatted = defined?(RedCloth) ? RedCloth.new(comment.comment).to_html : simple_format(comment.comment)
|
3
|
-
-
|
3
|
+
- collapsible = formatted.size > 150
|
4
4
|
- commentable = comment.commentable
|
5
5
|
|
6
6
|
%li.comment.highlight[comment]
|
@@ -15,12 +15,12 @@
|
|
15
15
|
= link_to(comment.user.full_name, user_path(comment.user)) + ","
|
16
16
|
%tt
|
17
17
|
= t(:added_note, value: timeago(comment.created_at) ).html_safe
|
18
|
-
- if
|
18
|
+
- if collapsible && can?(:read, commentable)
|
19
19
|
= " | "
|
20
20
|
= link_to_function(comment.collapsed? ? t(:more) : t(:less), "crm.flip_note_or_email(this, '#{t(:more)}', '#{t(:less)}')", class: "toggle")
|
21
21
|
|
22
22
|
- if can?(:read, commentable)
|
23
|
-
- if
|
23
|
+
- if collapsible
|
24
24
|
%dt{ hidden_if(comment.expanded?), id: dom_id(comment, :truncated) }
|
25
25
|
= truncated
|
26
26
|
%dt.textile{ hidden_if(comment.collapsed?), id: dom_id(comment, :formatted) }
|
@@ -18,18 +18,18 @@
|
|
18
18
|
%tr
|
19
19
|
%td
|
20
20
|
.label #{t :alt_email}:
|
21
|
-
= f.
|
21
|
+
= f.email_field :alt_email
|
22
22
|
%td= spacer
|
23
23
|
%td
|
24
24
|
.label #{t :mobile}:
|
25
|
-
= f
|
25
|
+
= phone_field_with_pattern(f, :mobile)
|
26
26
|
%tr
|
27
27
|
%td
|
28
28
|
= render "shared/address", f: f, asset: @contact, type: 'business', title: :address
|
29
29
|
%td= spacer
|
30
30
|
%td
|
31
31
|
.label #{t :fax}:
|
32
|
-
= f
|
32
|
+
= phone_field_with_pattern(f, :fax)
|
33
33
|
%div{style: "margin-top:6px"}
|
34
34
|
.check_box
|
35
35
|
= f.check_box :do_not_call, {}, true
|
@@ -35,11 +35,11 @@
|
|
35
35
|
|
|
36
36
|
- if contact.phone.present?
|
37
37
|
== #{t :phone_small}:
|
38
|
-
%b= contact.phone
|
38
|
+
%b= link_to_phone(contact.phone)
|
39
39
|
|
|
40
40
|
- if contact.mobile.present?
|
41
41
|
== #{t :mobile_small}:
|
42
|
-
%b= contact.mobile
|
42
|
+
%b= link_to_phone(contact.mobile)
|
43
43
|
|
|
44
44
|
= t(:added_ago, value: timeago(contact.created_at)).html_safe
|
45
45
|
- if contact.tags.present?
|
@@ -29,11 +29,11 @@
|
|
29
29
|
|
|
30
30
|
- if contact.phone.present?
|
31
31
|
== #{t :phone_small}:
|
32
|
-
%b= contact.phone
|
32
|
+
%b= link_to_phone(contact.phone)
|
33
33
|
|
|
34
34
|
- if contact.mobile.present?
|
35
35
|
== #{t :mobile_small}:
|
36
|
-
%b= contact.mobile
|
36
|
+
%b= link_to_phone(contact.mobile)
|
37
37
|
|
|
38
38
|
= t(:added_ago, value: timeago(contact.created_at)).html_safe
|
39
39
|
|
@@ -6,11 +6,11 @@
|
|
6
6
|
|
7
7
|
%div
|
8
8
|
- unless @contact.phone.blank?
|
9
|
-
== #{t :phone}: <b>#{@contact.do_not_call ? content_tag(:strike,
|
9
|
+
== #{t :phone}: <b>#{@contact.do_not_call ? content_tag(:strike, @contact.phone) : link_to_phone(@contact.phone)}</b><br />
|
10
10
|
- unless @contact.mobile.blank?
|
11
|
-
== #{t :mobile}: <b>#{@contact.do_not_call ? content_tag(:strike,
|
11
|
+
== #{t :mobile}: <b>#{@contact.do_not_call ? content_tag(:strike, @contact.mobile) : link_to_phone(@contact.mobile)}</b><br />
|
12
12
|
- unless @contact.fax.blank?
|
13
|
-
== #{t :fax}: <b>#{@contact.do_not_call ? content_tag(:strike,
|
13
|
+
== #{t :fax}: <b>#{@contact.do_not_call ? content_tag(:strike, @contact.fax) : link_to_phone(@contact.fax)}</b><br />
|
14
14
|
%div
|
15
15
|
- unless @contact.email.blank?
|
16
16
|
== #{t :email}: <b>#{link_to_email(@contact.email)}</b><br />
|
@@ -20,7 +20,7 @@
|
|
20
20
|
%dt
|
21
21
|
= link_to(account.website, account.website.to_url) + " | " if account.website.present?
|
22
22
|
= link_to_email(account.email) + " | " if account.email.present?
|
23
|
-
= t(:phone_small) + ": " + (account.toll_free_phone || account.phone)
|
23
|
+
= (t(:phone_small) + ": ").html_safe + link_to_phone(account.toll_free_phone || account.phone) + " | ".html_safe if account.toll_free_phone? || account.phone?
|
24
24
|
= t('pluralize.contact', account.contacts_count) + " | "
|
25
25
|
= t('pluralize.opportunity', account.opportunities_count)
|
26
26
|
- if account.tags.present?
|
@@ -2,6 +2,7 @@
|
|
2
2
|
%html
|
3
3
|
%head
|
4
4
|
%meta{ "http-equiv" => "Content-Type", content: "text/html; charset=utf-8" }
|
5
|
+
%meta{ name: "viewport", content: "width=device-width, initial-scale=1.0" }
|
5
6
|
%title Fat Free CRM
|
6
7
|
== <!-- #{controller.controller_name} : #{controller.action_name} -->
|
7
8
|
= stylesheet_link_tag :application
|
@@ -10,6 +11,9 @@
|
|
10
11
|
#{yield :stylesheet_includes}
|
11
12
|
%style= yield :styles
|
12
13
|
|
14
|
+
%link{ rel: "stylesheet", href: "https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" }
|
15
|
+
%script{ src: "https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" }
|
16
|
+
|
13
17
|
= javascript_include_tag :application
|
14
18
|
|
15
19
|
- unless tabless_layout? || %w(en-US en-GB).include?(I18n.locale.to_s)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
- edit ||= false
|
2
|
-
- collapsed =
|
2
|
+
- collapsed = false
|
3
3
|
= subtitle :lead_contact, collapsed, t(:contact_info)
|
4
4
|
.section
|
5
5
|
%small#lead_contact_intro{ hidden_if(!collapsed) }
|
@@ -17,11 +17,11 @@
|
|
17
17
|
%tr
|
18
18
|
%td
|
19
19
|
.label #{t :alt_email}:
|
20
|
-
= f.
|
20
|
+
= f.email_field :alt_email
|
21
21
|
%td= spacer
|
22
22
|
%td
|
23
23
|
.label #{t :mobile}:
|
24
|
-
= f
|
24
|
+
= phone_field_with_pattern(f, :mobile)
|
25
25
|
%tr
|
26
26
|
%td
|
27
27
|
= render "shared/address", f: f, asset: @lead, type: 'business', title: :address
|