wco_models 3.1.0.246 → 3.1.0.247

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/wco/collapse-expand.js +2 -1
  3. data/app/assets/javascripts/wco/tags.js +76 -0
  4. data/app/assets/stylesheets/wco/leads_leadsets.scss +5 -8
  5. data/app/assets/stylesheets/wco/main.scss +17 -0
  6. data/app/controllers/wco/leadsets_controller.rb +1 -0
  7. data/app/controllers/wco/office_actions_controller.rb +3 -0
  8. data/app/controllers/wco/tags_controller.rb +42 -5
  9. data/app/mailers/wco_email/application_mailer.rb +11 -3
  10. data/app/models/ability.rb +1 -1
  11. data/app/models/wco/leadset.rb +2 -1
  12. data/app/models/wco/log.rb +1 -1
  13. data/app/models/wco/profile.rb +8 -0
  14. data/app/models/wco_email/email_filter_condition.rb +3 -1
  15. data/app/models/wco_email/email_template.rb +3 -2
  16. data/app/models/wco_email/message.rb +20 -0
  17. data/app/models/wco_email/message_stub.rb +142 -5
  18. data/app/views/layouts/wco/application.haml +4 -3
  19. data/app/views/wco/_main_header.haml +1 -1
  20. data/app/views/wco/_select_all.haml +1 -1
  21. data/app/views/wco/galleries/_index.haml +1 -1
  22. data/app/views/wco/leads/_index.haml +9 -6
  23. data/app/views/wco/leadsets/_form.haml +10 -4
  24. data/app/views/wco/leadsets/_index.haml +10 -0
  25. data/app/views/wco/leadsets/index.haml +9 -10
  26. data/app/views/wco/leadsets/show.haml +24 -31
  27. data/app/views/wco/logs/_index.haml +1 -0
  28. data/app/views/wco/office_actions/_header.haml +2 -0
  29. data/app/views/wco/office_actions/index.haml +1 -1
  30. data/app/views/wco/profiles/_form.haml +62 -32
  31. data/app/views/wco/tags/_actions.haml +13 -0
  32. data/app/views/wco/tags/_header.haml +2 -0
  33. data/app/views/wco/tags/_index_chips.haml +2 -2
  34. data/app/views/wco/tags/_index_table.haml +11 -11
  35. data/app/views/wco/tags/_index_tree.haml +9 -0
  36. data/app/views/wco/tags/_menuitem.haml +2 -0
  37. data/app/views/wco/tags/index.haml +9 -2
  38. data/app/views/wco/tags/show.haml +43 -33
  39. data/config/routes.rb +5 -0
  40. data/lib/wco_models.rb +1 -1
  41. metadata +7 -3
  42. /data/app/views/wco/invoices/{_index_list.haml → _list.haml} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 129831dfab8ccdbb40ccdef3b12ff74fa4bb568b2d71456ea30095971992c905
4
- data.tar.gz: eba14a3662226e81fdeb2aaf77aa457df5155d9a913ee4e07da4ac6c7c5ccdeb
3
+ metadata.gz: 50588aa763ca804b8220051b0867eb3aa63c226c599ce732c9676a0b06e3808c
4
+ data.tar.gz: 442cb7d8a2dd4cf256b34ebac779a7958faed8e2c141672f35ef495a84b93a5d
5
5
  SHA512:
6
- metadata.gz: 24d783dc8abd8e9a74d6673aa4b834eaac7ae673a5529b03183b8c1d7f278bdbda4541aa84ebab4d3fcdfe0e9e79dcb991fcb8ec364cff3b5be0a6334d8cb158
7
- data.tar.gz: a56d2634a3c4f495fbe2acd7a15d9e6a9cb7a28e8da9067b76c26c39eee27d90ab89019a7dbd9c7b3f845f085584de69356d39d26f44eae1d8f66b5dd55a142f
6
+ metadata.gz: 62a65f04bfe70321ce6ccb231773db2368ede20502df293cde6d2223a1643aee2354b32ba31a70ae9b3a5eebc7ae752ee4b8e56bea6ec21b37f8052bb043724f
7
+ data.tar.gz: 8c1784a7644e1ff1a7a56ac895e9ab43cf0c9a01815596eb7a8a7bdd7d5b2fca79d8a4f6e8989f170a5a7653e87a9ea3c36a74d9e7e163a17bd0902306949101
@@ -27,4 +27,5 @@ $(function () {
27
27
  })
28
28
 
29
29
  console.log('Loaded wco/collapse-expand.js')
30
- }); // END
30
+
31
+ }); // END
@@ -0,0 +1,76 @@
1
+
2
+ // console.log('Loaded wco/tags.js')
3
+
4
+ $(function() {
5
+
6
+ $(".add-tag-many-btn").click(function(e) {
7
+ logg('here?')
8
+
9
+ if ( !confirm('Are you sure?') ) { return; }
10
+
11
+ const jwt_token = $("#Config").data('jwt-token')
12
+ const action_path = $(this).data('url')
13
+ const a_tag = $("select[name='a_tag']").val()
14
+ const out = []
15
+
16
+ $( $("input[type='checkbox'].i-sel:checked") ).each( idx => {
17
+ let val = $($("input[type='checkbox'].i-sel:checked")[idx]).val()
18
+ out.push(val)
19
+ })
20
+
21
+ let data = {
22
+ resource_ids: out,
23
+ jwt_token: jwt_token,
24
+ id: a_tag,
25
+ }
26
+ $.ajax({
27
+ url: action_path,
28
+ type: 'POST',
29
+ data: data,
30
+ success: e => {
31
+ logg((e||{}).responseText, 'Ok')
32
+ location.reload()
33
+ },
34
+ error: e => {
35
+ logg((e||{}).responseText, 'Err')
36
+ location.reload()
37
+ },
38
+ })
39
+
40
+ })
41
+
42
+
43
+ $(".remove-tag-btn").click(function(e) {
44
+ if ( !confirm('Are you sure?') ) { return; }
45
+
46
+ const jwt_token = $("#Config").data('jwt-token')
47
+ const action_path = $(this).data('url')
48
+ const emailtag = $("select[name='emailtag']").val()
49
+ const out = []
50
+
51
+ $( $(".conversations-list input[type='checkbox'].i-sel:checked") ).each( idx => {
52
+ let val = $($("input[type='checkbox'].i-sel:checked")[idx]).val()
53
+ out.push(val)
54
+ })
55
+
56
+ $.ajax({
57
+ url: action_path,
58
+ type: 'POST',
59
+ data: {
60
+ ids: out,
61
+ jwt_token: jwt_token,
62
+ slug: emailtag,
63
+ },
64
+ success: e => {
65
+ logg((e||{}).responseText, 'Ok')
66
+ location.reload()
67
+ },
68
+ error: e => {
69
+ logg((e||{}).responseText, 'Err')
70
+ location.reload()
71
+ },
72
+ })
73
+
74
+ })
75
+
76
+ })
@@ -1,13 +1,10 @@
1
1
 
2
- .Leads {
2
+ .--index,
3
+ .Leads,
4
+ .Leadsets, {
3
5
  border: 1px solid var(--wco-color-2);
4
- border-radius: .5em;
6
+ border-radius: 0.5em;
5
7
  padding: 0.5em;
8
+ margin-bottom: 0.5em;
6
9
  }
7
10
 
8
-
9
- .Leadsets {
10
- border: 1px solid var(--wco-color-2);
11
- border-radius: .5em;
12
- padding: 0.5em;
13
- }
@@ -70,6 +70,19 @@ table.bordered {
70
70
  }
71
71
  }
72
72
 
73
+ table {
74
+ thead.sticky {
75
+ background: #ccc;
76
+
77
+ th {
78
+ position: sticky;
79
+ top: 0;
80
+ background: white;
81
+ z-index: 10;
82
+ }
83
+ }
84
+ }
85
+
73
86
  /* C */
74
87
 
75
88
  .code,
@@ -84,6 +97,10 @@ textarea.monospace {
84
97
 
85
98
  /* D */
86
99
 
100
+ .d-flex {
101
+ align-items: baseline;
102
+ }
103
+
87
104
  .descr {
88
105
  border: 1px solid red;
89
106
  border-radius: 0.5em;
@@ -61,6 +61,7 @@ class Wco::LeadsetsController < Wco::ApplicationController
61
61
  end
62
62
 
63
63
  @leads = @leadset.leads.page( params[:leads_page] ).per( current_profile.per_page )
64
+ @logs = @leadset.logs.page( params[:logs_page] ).per( current_profile.per_page )
64
65
  @subscriptions = @leadset.subscriptions
65
66
  @invoices = @leadset.invoices
66
67
  end
@@ -42,6 +42,9 @@ class Wco::OfficeActionsController < Wco::ApplicationController
42
42
  def index
43
43
  authorize! :index, OA
44
44
  @oas = OA.all
45
+ if params[:status]
46
+ @oas = @oas.where( status: params[:status] )
47
+ end
45
48
  end
46
49
 
47
50
  def new
@@ -33,6 +33,17 @@ class Wco::TagsController < Wco::ApplicationController
33
33
  def index
34
34
  authorize! :index, Wco::Tag
35
35
  @tags = Wco::Tag.all
36
+
37
+ tags = Wco::Tag.all.to_a.group_by(&:parent_id)
38
+ build_tree = lambda do |parent_id|
39
+ (tags[parent_id] || []).sort_by { |tag| tag.slug.downcase }.map do |tag|
40
+ { tag: tag, sons: build_tree.call(tag.id) }
41
+ end
42
+ end
43
+ @tree = build_tree.call(nil)
44
+
45
+
46
+ @template = params[:template] || 'index_tree'
36
47
  end
37
48
 
38
49
  def new
@@ -50,6 +61,20 @@ class Wco::TagsController < Wco::ApplicationController
50
61
  redirect_to request.referrer
51
62
  end
52
63
 
64
+ def add_to_many
65
+ @tag = Wco::Tag.find params[:id]
66
+ resources = params[:resource].constantize.find params[:resource_ids]
67
+ authorize! :update, @tag
68
+
69
+ flags = []
70
+ resources.each do |resource|
71
+ resource.tags.push @tag
72
+ flags.push resource.save
73
+ end
74
+ flash_notice flags
75
+ # redirect_to request.referrer
76
+ end
77
+
53
78
  def remove_from
54
79
  @tag = Wco::Tag.find params[:id]
55
80
  resource = params[:resource].constantize.find params[:resource_id]
@@ -61,15 +86,27 @@ class Wco::TagsController < Wco::ApplicationController
61
86
  redirect_to request.referrer
62
87
  end
63
88
 
89
+ def remove_from_many
90
+ @tag = Wco::Tag.find params[:id]
91
+ resources = params[:resource].constantize.find params[:resource_ids]
92
+ authorize! :update, @tag
93
+
94
+ flags = []
95
+ resources.each do |resource|
96
+ resource.tags.delete @tag
97
+ flags.push resource.save
98
+ end
99
+ flash_notice flags
100
+ # redirect_to request.referrer
101
+ end
102
+
64
103
  def show
65
104
  @tag = Wco::Tag.find params[:id]
66
105
  authorize! :show, @tag
67
106
 
68
- @galleries = @tag.galleries(
69
- ).page( params[:galleries_page] ).per( current_profile.per_page )
70
-
71
- @reports = @tag.reports(
72
- ).page( params[:reports_page] ).per( current_profile.per_page )
107
+ @galleries = @tag.galleries.page( params[:galleries_page] ).per( current_profile.per_page )
108
+ @leadsets = @tag.leadsets.page( params[::Wco::Leadset::PAGE_PARAM_NAME] ).per( current_profile.per_page )
109
+ @reports = @tag.reports.page( params[:reports_page] ).per( current_profile.per_page )
73
110
 
74
111
  end
75
112
 
@@ -51,14 +51,22 @@ class WcoEmail::ApplicationMailer < ActionMailer::Base
51
51
  subject: rendered_subject,
52
52
  })
53
53
 
54
+ profile = Wco::Profile.find_by email: @ctx.from_email
55
+ delivery_options = {
56
+ user_name: profile.smtp_username,
57
+ password: profile.smtp_password,
58
+ address: profile.smtp_host,
59
+ post: profile.smtp_port,
60
+ };
61
+
54
62
  mail( from: @ctx.from_email,
55
63
  to: @ctx.to_email,
56
64
  cc: @ctx.cc,
57
- ## 2024-07-30 I'm no longer sending these to google.
58
- # bcc: "poxlovibb1@gmail.com",
65
+ bcc: DEFAULT_BCC,
59
66
  subject: rendered_subject,
60
67
  body: rendered_str,
61
- content_type: "text/html" )
68
+ content_type: "text/html",
69
+ delivery_method_options: delivery_options )
62
70
  end
63
71
 
64
72
  def shared_galleries profiles, gallery
@@ -10,7 +10,7 @@ class Ability
10
10
 
11
11
  if user
12
12
 
13
- if [ ENV['EMAIL'] ].include? user.email
13
+ if [ ENV['EMAIL'], 'piousbox@gmail.com' ].include? user.email
14
14
  can :manage, :all
15
15
  end
16
16
 
@@ -27,7 +27,8 @@ class Wco::Leadset
27
27
  find_or_create_by( company_url: _domain )
28
28
  end
29
29
 
30
-
30
+ field :company_name # for chatgpt scraping
31
+ has_many :logs, inverse_of: :obj
31
32
 
32
33
  field :email
33
34
  index({ email: 1 }, { name: 'email' })
@@ -9,7 +9,7 @@ class Wco::Log
9
9
  field :label, type: :string ## can be the stream: 'stdout' or 'stderr'
10
10
  field :message, type: :string ## can be json
11
11
 
12
- belongs_to :obj, polymorphic: true, optional: true
12
+ belongs_to :obj, polymorphic: true, optional: true # eg site
13
13
 
14
14
  has_and_belongs_to_many :tags
15
15
 
@@ -9,6 +9,8 @@ class Wco::Profile
9
9
  index({ email: 1 }, { name: 'email' })
10
10
  validates :email, presence: true, uniqueness: true
11
11
 
12
+ field :name
13
+
12
14
 
13
15
  field :per_page, type: :integer, default: 25
14
16
  field :show_n_thumbs, type: :integer, default: 8
@@ -23,6 +25,12 @@ class Wco::Profile
23
25
  field :schwab_exec_id_token, type: :string
24
26
  field :schwab_account_hash, type: :string
25
27
 
28
+ field :smtp_enabled, type: Boolean
29
+ field :smtp_host
30
+ field :smtp_username
31
+ field :smtp_password
32
+ field :smtp_port
33
+
26
34
 
27
35
  has_many :newsvideos, class_name: 'Wco::Newsvideo'
28
36
  has_many :reports, class_name: 'Wco::Report'
@@ -14,6 +14,8 @@ class WcoEmail::EmailFilterCondition
14
14
 
15
15
  field :matchtype, type: String
16
16
  validates :matchtype, presence: true, inclusion: WcoEmail::EmailFilter::MATCHTYPE_OPTS
17
+ def operator; matchtype; end
18
+ def operator= a; matchtype = a; end
17
19
 
18
20
  field :value
19
21
  validates :value, presence: true
@@ -28,7 +30,7 @@ class WcoEmail::EmailFilterCondition
28
30
  if leadset.tags.include?( this_tag )
29
31
  ;
30
32
  else
31
- reason = "{email_skip_filter ? 'skip_' : ''}condition leadset not-has-tag #{this_tag} NOT met"
33
+ reason = "#{email_skip_filter ? 'skip_' : ''}condition leadset not-has-tag #{this_tag} NOT met"
32
34
  end
33
35
  end
34
36
  when WcoEmail::FIELD_TO
@@ -103,8 +103,9 @@ class WcoEmail::EmailTemplate
103
103
  ];
104
104
  field :from_email
105
105
  def self.from_emails_list
106
- # [ [nil, nil] ] + FROM_EMAILS.map { |i| [i, i] }
107
- FROM_EMAILS_2
106
+ [[nil,nil]] + Wco::Profile.where( smtp_enabled: true ).map do |p|
107
+ [ "#{p.name} <#{p.email}>", p.email ]
108
+ end
108
109
  end
109
110
 
110
111
  SIGNATURE = <<~AOL
@@ -229,6 +229,26 @@ class WcoEmail::Message
229
229
  end
230
230
  end
231
231
 
232
+ def save_attachment_postal att
233
+ if att['content_type'].include? 'image'
234
+ photo = ::Wco::Photo.new({
235
+ content_type: att['content_type'],
236
+ email_message_id: self.id,
237
+ image_data: att['data'],
238
+ original_filename: att['filename'],
239
+ })
240
+ photo.decode_base64_image ## _TODO: why?
241
+ puts! photo.save, 'saved image attachment?'
242
+ else
243
+ asset = ::Wco::Asset.new({
244
+ email_message: self,
245
+ filename: att['filename'],
246
+ object: att['data'],
247
+ })
248
+ puts! asset.save, 'saved non-img asset?'
249
+ end
250
+ end
251
+
232
252
  def save_attachment att, filename: "no-filename-specified"
233
253
  config = JSON.parse(stub.config)
234
254
  if defined?( config['process_images'] ) &&
@@ -45,6 +45,144 @@ class WcoEmail::MessageStub
45
45
  {}
46
46
  AOL
47
47
 
48
+ def do_process_json
49
+ stub = self
50
+ @client ||= Aws::S3::Client.new(::SES_S3_CREDENTIALS)
51
+
52
+ json = JSON.parse( @client.get_object( bucket: stub.bucket, key: stub.object_key ).body.read )
53
+ subject = json['subject']
54
+ message_id = json['message_id']
55
+
56
+ ## Conversation
57
+ if json['in_reply_to']
58
+ in_reply_to_msg = WcoEmail::Message.where({ message_id: json['in_reply_to'] }).first
59
+ if !in_reply_to_msg
60
+ @conv = WcoEmail::Conversation.find_or_create_by({
61
+ subject: subject,
62
+ })
63
+ in_reply_to_msg = WcoEmail::Message.find_or_create_by({
64
+ message_id: json['in_reply_to'],
65
+ conversation: @conv,
66
+ })
67
+ end
68
+ @conv = in_reply_to_msg.conversation
69
+ else
70
+ @conv = WcoEmail::Conversation.unscoped.find_or_create_by({
71
+ subject: subject,
72
+ })
73
+ @conv.deleted_at = nil
74
+ end
75
+
76
+ ## Leadset, Lead
77
+ from = json['mail_from'] || "nobody@unknown-doma.in"
78
+ @lead = Wco::Lead.find_or_create_by_email( from )
79
+ @conv.leads.push @lead
80
+ @leadset = Wco::Leadset.from_email from
81
+ @conv.leadsets.push @leadset
82
+
83
+ ## message
84
+ @message = WcoEmail::Message.create!({
85
+ stub: stub,
86
+ conversation: @conv,
87
+ lead: @lead,
88
+
89
+ message_id: message_id,
90
+ in_reply_to_id: json['in_reply_to'],
91
+ object_key: stub.object_key,
92
+
93
+ subject: subject,
94
+ date: json['date'].to_s,
95
+
96
+ from: from,
97
+
98
+ to: json['to'],
99
+ tos: [ json['to'] ],
100
+
101
+ cc: json['cc'],
102
+ ccs: [ json['cc'] ],
103
+
104
+ part_html: json['html_body'],
105
+ part_txt: json['plain_body'],
106
+ })
107
+
108
+ ## Attachments
109
+ json['attachments'].each do |att|
110
+ @message.save_attachment_postal( att )
111
+ end
112
+
113
+ ## _TODO
114
+ # the_mail.cc&.each do |cc|
115
+ # Wco::Lead.find_or_create_by_email( cc )
116
+ # end
117
+
118
+ @conv.update_attributes({
119
+ status: WcoEmail::Conversation::STATUS_UNREAD,
120
+ latest_at: json['date'].to_s || Time.now.to_datetime,
121
+ from_emails: ( @conv.from_emails + [ from ]).uniq,
122
+ preview: @message.preview_str,
123
+ })
124
+
125
+ ## tags
126
+ @conv.tags.push Wco::Tag.inbox
127
+ @conv.tags.push stub.tags
128
+ @conv.save
129
+
130
+
131
+ ## Actions & Filters
132
+ email_filters = WcoEmail::EmailFilter.all
133
+ email_filters.each do |filter|
134
+ reason = nil
135
+ if filter.from_regex.present? && @message.from.downcase.match( filter.from_regex )
136
+ reason = 'from_regex'
137
+ end
138
+ if filter.from_exact.present? && @message.from.downcase.include?( filter.from_exact.downcase )
139
+ reason = 'from_exact'
140
+ end
141
+ if filter.body_exact.present? && @message.part_html&.include?( filter.body_exact )
142
+ reason = 'body_exact'
143
+ end
144
+ if filter.subject_regex.present? && @message.subject.match( filter.subject_regex )
145
+ reason = 'subject_regex'
146
+ end
147
+ if filter.subject_exact.present? && @message.subject.downcase.include?( filter.subject_exact.downcase )
148
+ reason = 'subject_exact'
149
+ end
150
+
151
+ filter.conditions.each do |scond|
152
+ reason ||= scond.apply(leadset: @leadset, message: @message )
153
+ end
154
+
155
+ if reason
156
+ puts! "Applying filter #{filter} to conv #{@message.conversation} for matching #{reason}" if DEBUG
157
+
158
+ ## skip
159
+ skip_reason = nil
160
+ if filter.skip_to_exact.present? && @message.to&.downcase.include?( filter.skip_to_exact.downcase )
161
+ skip_reason = 'skip_to_exact'
162
+ end
163
+ if filter.skip_from_regex.present? && @message.from.downcase.match( filter.skip_from_regex )
164
+ skip_reason = 'skip_from_regex'
165
+ end
166
+
167
+ filter.skip_conditions.each do |scond|
168
+ skip_reason ||= scond.apply(leadset: @leadset, message: @message )
169
+ end
170
+
171
+ if skip_reason
172
+ puts! "NOT Applying filter #{filter} to conv #{@message.conversation} for matching #{skip_reason}" if DEBUG
173
+ else
174
+ @message.apply_filter( filter )
175
+ end
176
+ end
177
+ end
178
+
179
+ stub.update_attributes({ status: WcoEmail::MessageStub::STATUS_PROCESSED })
180
+
181
+ ## Notification
182
+ ## _TODO
183
+ end
184
+
185
+
48
186
  def do_process
49
187
  if Rails.env.production?
50
188
  @client ||= Aws::S3::Client.new({
@@ -101,7 +239,6 @@ class WcoEmail::MessageStub
101
239
  conv.leads.push lead
102
240
  leadset = Wco::Leadset.from_email from
103
241
  conv.leadsets.push leadset
104
- # conv.save
105
242
 
106
243
  message = WcoEmail::Message.unscoped.where( message_id: message_id ).first
107
244
  if message
@@ -203,8 +340,8 @@ class WcoEmail::MessageStub
203
340
  reason = 'subject_exact'
204
341
  end
205
342
 
206
- filter.conditions.each do |scond|
207
- reason ||= scond.apply(leadset: leadset, message: @message )
343
+ filter.conditions.each do |cond|
344
+ reason ||= cond.apply(leadset: leadset, message: @message )
208
345
  end
209
346
 
210
347
  if reason
@@ -219,8 +356,8 @@ class WcoEmail::MessageStub
219
356
  skip_reason = 'skip_from_regex'
220
357
  end
221
358
 
222
- filter.skip_conditions.each do |scond|
223
- skip_reason ||= scond.apply(leadset: leadset, message: @message )
359
+ filter.skip_conditions.each do |skip_cond|
360
+ skip_reason ||= skip_cond.apply(leadset: leadset, message: @message )
224
361
  end
225
362
 
226
363
  if skip_reason
@@ -39,9 +39,10 @@
39
39
  = render '/wco/main_footer'
40
40
  = render '/wco/analytics' if Rails.env.production?
41
41
 
42
- %script{:src => "https://js.stripe.com/v3/"}
43
- :javascript
44
- stripe = Stripe('#{::STRIPE_PK}', { apiVersion: '2020-08-27' });
42
+ - if defined?( ::STRIPE_PK )
43
+ %script{:src => "https://js.stripe.com/v3/"}
44
+ :javascript
45
+ stripe = Stripe('#{::STRIPE_PK}', { apiVersion: '2020-08-27' });
45
46
 
46
47
  -# = render '/wco/application/auth_widget'
47
48
 
@@ -3,7 +3,7 @@
3
3
 
4
4
  -# %i.fa.fa-compress.collapse-expand#collapseHeaderMain
5
5
  -# Wco Suite
6
- .maxwidth
6
+ .a{ style: 'max-width: 1200px; margin: auto; ' }
7
7
 
8
8
  %i.fa.fa-compress.collapse-expand#collapseHeaderModels
9
9
  Wco Suite
@@ -1,5 +1,5 @@
1
1
 
2
2
  .d-flex
3
- .label.mini All
3
+ %label.mini All
4
4
  = check_box_tag :select_all
5
5
  .n-selected -
@@ -7,7 +7,7 @@
7
7
  - galleries ||= @galleries
8
8
 
9
9
 
10
- .galleries--index.maxwidth
10
+ .Galleries.galleries--index.--index
11
11
  = render '/wco/galleries/header_mini', galleries: galleries
12
12
 
13
13
  .padded
@@ -1,7 +1,10 @@
1
1
 
2
- %ul.leads--list
3
- - leads.each do |lead|
4
- %li.item
5
- = image_tag lead.photo.photo.url(:thumb) if lead.photo
6
- = link_to lead, lead_path(lead)
7
- = link_to '[~]', edit_lead_path(lead)
2
+ .Leads.leads--index.--index
3
+ <b>Leads:</b>
4
+ %ul
5
+ - leads.each do |lead|
6
+ %li.item
7
+ = image_tag lead.photo.photo.url(:thumb) if lead.photo
8
+ = link_to lead, wco.lead_path(lead)
9
+ = link_to '[~]', wco.edit_lead_path(lead)
10
+ <b>Tags:</b> #{ lead.tags.map { |tag| tag.to_s } }
@@ -3,12 +3,18 @@
3
3
 
4
4
  = form_for leadset, :as => :leadset, :url => url do |f|
5
5
 
6
- .field
7
- = f.label :company_url
8
- = f.text_field :company_url
6
+ .row
7
+ .col-6
8
+ .field
9
+ %label company_url
10
+ = f.text_field :company_url
11
+ .col-6
12
+ .field
13
+ %label company_name
14
+ = f.text_field :company_name
9
15
 
10
16
  .field
11
- = f.label :email
17
+ %label email
12
18
  = f.text_field :email
13
19
  .field
14
20
  %label mangle_subject
@@ -0,0 +1,10 @@
1
+
2
+ .Leadsets.--index
3
+ .d-inline <b>Leadsets (#{leadsets.length}):</b>
4
+ = paginate leadsets, :param_name => ::Wco::Leadset::PAGE_PARAM_NAME, :views_prefix => 'wco'
5
+ %ul
6
+ - leadsets.each do |ls|
7
+ %li
8
+ = link_to ls.company_url, wco.leadset_path( ls )
9
+ = link_to '[~]', wco.edit_leadset_path( ls )
10
+ <b>Tags:</b> #{ ls.tags.map { |tag| tag.to_s } }
@@ -5,27 +5,26 @@
5
5
  (#{Wco::Leadset.all.length})
6
6
  = link_to raw("<i class='fa fa-plus-square'></i>"), new_leadset_path
7
7
 
8
- .actions
9
- %label.select-all
10
- = check_box_tag :select_all
11
- select all
12
- .n-selected -
13
- = button_tag 'Delete', method: :delete, class: 'delete-btn'
8
+ .actions.d-flex
9
+ .a.mr-2= button_tag 'Delete', method: :delete, class: 'delete-btn'
10
+ = render 'wco/tags/actions', resource: 'Wco::Leadset'
14
11
 
15
12
  .float-right= render '/wco/search', path: leadsets_path
16
13
  = paginate @leadsets, :param_name => :leadsets_page, :views_prefix => 'wco'
17
14
  %table.bordered
18
15
  %thead
19
16
  %tr
20
- %th &nbsp;
21
- %th.company-url Company Url
17
+ %th.select-all.nosort
18
+ = render '/wco/select_all'
19
+ %th.company-url company_url
22
20
  %th Tags
23
21
  %th Leads
24
22
 
25
23
  %tbody
26
- - @leadsets.each_with_index do |leadset, idx1|
24
+ - @leadsets.each_with_index do |leadset, idx|
27
25
  %tr
28
- %td
26
+ %td.select-all
27
+ .gray= idx+1
29
28
  = check_box_tag 'leadset_ids[]', leadset.id, nil, { class: 'i-sel' }
30
29
 
31
30
  %td
@@ -11,38 +11,35 @@
11
11
  %li <b>Email:</b> #{@leadset.email}
12
12
  %li <b>company_url</b>: #{@leadset.company_url}
13
13
  %li <b>customer_id</b>: #{@leadset.customer_id}
14
- -# tags
15
14
  .col-6
15
+ -# tags
16
16
  Tags:
17
17
  = render '/wco/tags/index_chips', tags: @leadset.tags
18
18
 
19
- %hr
20
- .row
21
- -# serverhosts
22
- .col-6.border-right
23
- = render 'wco_hosting/serverhosts/list', serverhosts: @leadset.serverhosts
24
- .col-6
25
- &nbsp;
26
-
27
- %hr
28
- .row
29
- -# environments
30
- .col-md-6
31
- - if defined?( wco_hosting )
19
+ -# false
20
+ - if false && defined?( wco_hosting )
21
+ %hr
22
+ .row
23
+ .col-8.border-right
24
+ -# serverhosts
25
+ = render 'wco_hosting/serverhosts/list', serverhosts: @leadset.serverhosts
26
+ .col-4
27
+ -# environments
32
28
  .header
33
29
  %h5.title Environments (#{@leadset.environments.length}) #{link_to '[+]', wco_hosting.new_environment_path({ leadset_id: @leadset.id }) }
34
30
  %ul
35
31
  - @leadset.environments.each do |env|
36
32
  %li= env
33
+
34
+ -# appliances
35
+ .header
36
+ %h5.title Appliances (#{@leadset.appliances.length})
37
+ = render 'wco_hosting/appliances/list', appliances: @leadset.appliances
38
+
37
39
  %hr
38
40
  .row
39
- -# appliances
40
41
  .col-md-6.border-right
41
- .header
42
- %h5.title Appliances (#{@leadset.appliances.length})
43
-
44
- -# subscriptions
45
- .col-md-6
42
+ -# subscriptions
46
43
  .header
47
44
  %h5.title.collapse-expand#subscriptionsList
48
45
  Subscriptions (#{@subscriptions.length})
@@ -56,18 +53,15 @@
56
53
  %tr
57
54
  %td= i.product.name
58
55
  %td= i.price
59
-
60
- %hr
61
- .row
62
- -# invoices
63
- .col-md-12
56
+ .col-md-6
57
+ -# invoices
64
58
  .header
65
59
  %h5.title.collapse-expand#invoicesList
66
60
  Invoices (#{@leadset.invoices.length})
67
61
  = link_to '[+stripe]', new_invoice_stripe_path({ leadset_id: @leadset.id })
68
62
  = link_to '[+pdf]', new_invoice_pdf_path({ leadset_id: @leadset.id })
69
63
 
70
- = render '/wco/invoices/index_list', invoices: @invoices
64
+ = render '/wco/invoices/list', invoices: @invoices
71
65
 
72
66
  %p Next invoice number: #{@leadset.next_invoice_number}
73
67
 
@@ -78,11 +72,10 @@
78
72
  = check_box_tag 'replace'
79
73
  = submit_tag 'Go >', data: { confirm: 'Are you sure?' }
80
74
 
81
- %hr
82
- .row
83
- -# leads
75
+ .row.mt-3
84
76
  .col-12
85
- .header
86
- %h5.title.collapse-expand#leads Leads
87
77
  = render '/wco/leads/index', leads: @leads
88
78
 
79
+ .row.mt-3
80
+ .col-12
81
+ = render '/wco/logs/index', logs: @logs
@@ -2,6 +2,7 @@
2
2
  - logs ||= @logs
3
3
 
4
4
  .logs--index
5
+ Logs:
5
6
  = form_tag logs_bulkop_path, method: :delete do
6
7
  = hidden_field_tag :authenticity_token, form_authenticity_token
7
8
  .d-flex-row
@@ -1,5 +1,7 @@
1
1
 
2
2
  = link_to "Office Actions (#{Wco::OfficeAction.all.length})", wco.office_actions_path
3
+ = link_to '[A]', wco.office_actions_path(status: 'active')
4
+ &nbsp;
3
5
  .inline-search
4
6
  = form_tag wco.office_actions_path, method: :get do
5
7
  = text_field_tag :q
@@ -1,7 +1,7 @@
1
1
 
2
2
  .office-actions-index.maxwidth
3
3
 
4
- %h5 Office actions (#{@oas.length})
4
+ %h5.text-center Office actions `#{params[:status]}` (#{@oas.length})
5
5
 
6
6
  = render 'index', oas: @oas
7
7
 
@@ -1,41 +1,71 @@
1
1
 
2
2
  .profiles--form
3
3
  = form_for profile do |f|
4
- .field
5
- %label email
6
- = f.text_field :email
7
-
8
- .field
9
- %label role
10
- = f.select :role, options_for_select(Wco::Profile.roles_list, selected: profile.role )
11
-
12
- .field
13
- %label schwab_access_token
14
- = f.text_field :schwab_access_token
15
- .field
16
- %label schwab_refresh_token
17
- = f.text_field :schwab_refresh_token
18
- .field
19
- %label schwab_id_token
20
- = f.text_field :schwab_id_token
21
-
22
- %br
23
- %br
24
- .field
25
- %label schwab_exec_access_token
26
- = f.text_field :schwab_exec_access_token
27
- .field
28
- %label schwab_exec_refresh_token
29
- = f.text_field :schwab_exec_refresh_token
30
- .field
31
- %label schwab_exec_id_token
32
- = f.text_field :schwab_exec_id_token
4
+
5
+ .row
6
+
7
+ .field
8
+ %label email
9
+ = f.text_field :email
10
+
11
+ .field
12
+ %label name
13
+ = f.text_field :name
14
+
15
+ .field
16
+ %label role
17
+ = f.select :role, options_for_select(Wco::Profile.roles_list, selected: profile.role )
18
+
19
+ .field
20
+ %label leadset
21
+ = f.select :leadset, options_for_select(Wco::Leadset.list, selected: profile.leadset_id )
33
22
 
34
23
  %hr
24
+ .row
25
+ .col-6
26
+ .field
27
+ %label schwab_access_token
28
+ = f.text_field :schwab_access_token
29
+ .field
30
+ %label schwab_refresh_token
31
+ = f.text_field :schwab_refresh_token
32
+ .field
33
+ %label schwab_id_token
34
+ = f.text_field :schwab_id_token
35
+ .col-6
36
+ .field
37
+ %label schwab_exec_access_token
38
+ = f.text_field :schwab_exec_access_token
39
+ .field
40
+ %label schwab_exec_refresh_token
41
+ = f.text_field :schwab_exec_refresh_token
42
+ .field
43
+ %label schwab_exec_id_token
44
+ = f.text_field :schwab_exec_id_token
45
+
46
+ %hr
47
+ .row
48
+ .col-2
49
+ .field
50
+ %label smtp_enabled
51
+ = f.check_box :smtp_enabled
52
+ .col-10
53
+ .field
54
+ %label smtp_host
55
+ = f.text_field :smtp_host
56
+
57
+ .field
58
+ %label smtp_username
59
+ = f.text_field :smtp_username
60
+
61
+ .field
62
+ %label smtp_password
63
+ = f.text_field :smtp_password
64
+
65
+ .field
66
+ %label smtp_port
67
+ = f.text_field :smtp_port
35
68
 
36
- .field
37
- %label leadset
38
- = f.select :leadset, options_for_select(Wco::Leadset.list, selected: profile.leadset_id )
39
69
 
40
70
  .actions
41
71
  = f.submit 'Go'
@@ -0,0 +1,13 @@
1
+
2
+
3
+ .tags--actions.bordered.inline-block.mb-2.descr
4
+ %label tags/_actions
5
+ .d-inline-block
6
+ = select_tag :a_tag, options_for_select(@tags_list), class: 'select2'
7
+ %a.btn.bordered.add-tag-many-btn{ href: "javascript: void(0)", data: { url: wco.add_tag_to_many_path(resource) } }
8
+ %i.material-icons add
9
+ tag
10
+ %a.btn.bordered.rm-tag-many-btn{ href: "javascript: void(0)", data: { url: wco.rm_tag_from_many_path(resource) } }
11
+ %i.material-icons remove
12
+ tag
13
+
@@ -2,4 +2,6 @@
2
2
  .header
3
3
  %h5.title
4
4
  Tags (#{Wco::Tag.all.length})&nbsp;
5
+ = link_to '[#]', wco.tags_path(template: 'index_table')
6
+ = link_to '[/|\\]', wco.tags_path(template: 'index_tree')
5
7
  = link_to '[+]', wco.new_tag_path
@@ -10,11 +10,11 @@
10
10
  = check_box_tag 'tag_ids[]', tag.id
11
11
  = link_to tag.slug, wco.tag_path( tag )
12
12
  - if defined? resource
13
- .mini.d-inline-block= button_to 'x', remove_tag_from_path(id: tag.id, resource: resource.class, resource_id: resource.id, ), method: 'delete', data: { confirm: 'Are you sure?' }
13
+ .mini.d-inline-block= button_to 'x', wco.remove_tag_from_path(id: tag.id, resource: resource.class, resource_id: resource.id, ), method: 'delete', data: { confirm: 'Are you sure?' }
14
14
 
15
15
  - if defined? resource
16
16
  .d-inline-block
17
- = form_tag add_tag_to_path( resource: resource.class, resource_id: resource.id ) do
17
+ = form_tag wco.add_tag_to_path( resource: resource.class, resource_id: resource.id ) do
18
18
  .d-inline-block= select_tag :id, options_for_select( @tags_list ), class: 'select2'
19
19
  = submit_tag '+Tag', data: { confirm: 'Are you sure?' }
20
20
 
@@ -1,21 +1,21 @@
1
1
 
2
2
  %table.bordered.tags--index.maxwidth
3
- %thead
3
+ %thead.sticky
4
4
  %tr
5
- %td &nbsp;
6
- %td N Conv
7
- %td N Stubs
5
+ %th &nbsp;
6
+ %th N Conv
7
+ %th N Stubs
8
8
 
9
- %td N Headlines
9
+ %th N Headlines
10
10
 
11
- %td N Galleries
12
- %td N Reports
13
- %td N Videos
11
+ %th N Galleries
12
+ %th N Reports
13
+ %th N Videos
14
14
 
15
- %td N leadsets
16
- %td N Leads
15
+ %th N Leadsets
16
+ %th N Leads
17
17
 
18
- %td N Logs
18
+ %th N Logs
19
19
  %tbody
20
20
  - tags.each do |tag|
21
21
  %tr
@@ -0,0 +1,9 @@
1
+
2
+ %ul
3
+ - nodes.each do |node|
4
+ %li
5
+ = link_to '[~]', wco.edit_tag_path(node[:tag][:id])
6
+ = link_to node[:tag].slug, wco.tag_path(node[:tag][:id])
7
+
8
+ - if node[:sons].present?
9
+ = render 'wco/tags/index_tree', nodes: node[:sons]
@@ -1,6 +1,8 @@
1
1
 
2
2
  .d-flex
3
3
  = link_to "Tags (#{Wco::Tag.all.length})", wco.tags_path
4
+ = link_to '[#]', wco.tags_path(template: 'index_table')
5
+ = link_to '[/|\\]', wco.tags_path(template: 'index_tree')
4
6
  &nbsp;
5
7
  .inline-search
6
8
  = form_tag wco.tags_path, method: :get do
@@ -1,9 +1,16 @@
1
1
 
2
2
  .tags-index.padded
3
3
  .header
4
- %h5.title Tags (#{@tags.length})
4
+ %h5.title
5
+ Tags #{@template} (#{@tags.length})&nbsp;
6
+ = link_to( '[#]', url_for(params.to_unsafe_h.merge(template: "index_table")) )
7
+ &nbsp;
8
+ = link_to( '[/|\\]', url_for(params.to_unsafe_h.merge(template: "index_tree")) )
5
9
 
6
- = render 'index_table', tags: @tags
10
+ - if 'index_table' == @template
11
+ = render @template, tags: @tags
12
+ - else
13
+ = render @template, nodes: @tree
7
14
 
8
15
  %hr
9
16
 
@@ -4,36 +4,46 @@
4
4
  .text-center
5
5
  %h5 Tag `#{@tag.slug}`
6
6
 
7
- %ul
8
- - if defined? wco_email
9
- %li.d-flex
10
- <b>Conversations (#{@tag.conversations.length}):&nbsp;</b>
11
- - if @tag.conversations.present?
12
- .Conversations
13
- = render '/wco_email/conversations/table', convs: @tag.conversations.page( params[::WcoEmail::Conversation::PAGE_PARAM_NAME] )
14
- %li.d-flex
15
- <b>EmailTemplates (#{@tag.email_templates.length}):&nbsp;</b>
16
- - if @tag.email_templates.present?
17
- .EmailTemplates
18
- = render '/wco_email/email_templates/index_mini', email_templates: @tag.email_templates
19
- %li.d-flex
20
- <b>Message Stubs (#{@tag.message_stubs.length}):&nbsp;</b>
21
- = render '/wco_email/message_stubs/index', stubs: @tag.message_stubs.page( params[::WcoEmail::MessageStub::PAGE_PARAM_NAME] )
22
-
23
- %li.d-flex
24
- <b>Galleries (#{@galleries.length}):&nbsp;</b>
25
- = render '/wco/galleries/index', galleries: @galleries.page( params[::Wco::Gallery::PAGE_PARAM_NAME] ), config: { skip_tags: true }
26
- %li.d-flex
27
- <b>Headlines (#{@tag.headlines.length}):&nbsp;</b>
28
- = render '/wco/headlines/index', headlines: @tag.headlines.page( params[::Wco::Headline::PAGE_PARAM_NAME] )
29
-
30
- %li.d-flex
31
- <b>Leadsets (#{@tag.leadsets.length}):&nbsp;</b>
32
- = render '/wco/leadsets/index_list', leadsets: @tag.leadsets.page( params[::Wco::Leadset::PAGE_PARAM_NAME] )
33
- %li.d-flex
34
- <b>Leads (#{@tag.leads.length}):&nbsp;</b>
35
- = render '/wco/leads/index', leads: @tag.leads.page( params[::Wco::Lead::PAGE_PARAM_NAME] )
36
-
37
- %li.d-flex
38
- <b>Reports (#{@reports.length}):&nbsp;</b>
39
- = render '/wco/reports/index', reports: @reports.page( params[::Wco::Report::PAGE_PARAM_NAME] ), config: { skip_tags: true }
7
+ - if defined?( wco_email )
8
+ .a
9
+ - if @tag.conversations.present?
10
+ %i.fa.fa-compress.collapse-expand#conversations
11
+ Conversations (#{@tag.conversations.length}):
12
+ .descr= render '/wco_email/conversations/table', convs: @tag.conversations.page( params[::WcoEmail::Conversation::PAGE_PARAM_NAME] )
13
+
14
+ .a
15
+ - if @tag.email_templates.present?
16
+ %hr
17
+ %i.fa.fa-compress.collapse-expand#emailTemplates
18
+ EmailTemplates (#{@tag.email_templates.length}):
19
+ .EmailTemplates
20
+ = render '/wco_email/email_templates/index_mini', email_templates: @tag.email_templates
21
+
22
+ .a
23
+ %hr
24
+ %i.fa.fa-compress.collapse-expand#messageStubs
25
+ Message Stubs (#{@tag.message_stubs.length}):
26
+ = render '/wco_email/message_stubs/index', stubs: @tag.message_stubs.page( params[::WcoEmail::MessageStub::PAGE_PARAM_NAME] )
27
+
28
+ %hr
29
+ %i.fa.fa-compress.collapse-expand#galleries
30
+ Galleries (#{@galleries.length}):
31
+ = render '/wco/galleries/index', galleries: @galleries.page( params[::Wco::Gallery::PAGE_PARAM_NAME] ), config: { skip_tags: true }
32
+
33
+ %hr
34
+ %i.fa.fa-compress.collapse-expand#leadsets
35
+ Leadsets: (#{@leadsets.length}):
36
+ = render '/wco/leadsets/index', leadsets: @leadsets
37
+
38
+ %hr
39
+ %i.fa.fa-compress.collapse-expand#leads
40
+ Leads: (#{@tag.leads.length}):
41
+ = render '/wco/leads/index', leads: @tag.leads.page( params[::Wco::Lead::PAGE_PARAM_NAME] )
42
+
43
+ %hr
44
+ %i.fa.fa-compress.collapse-expand#reports
45
+ Reports: (#{@reports.length}):
46
+ .d-flex
47
+ = render '/wco/reports/index', reports: @reports.page( params[::Wco::Report::PAGE_PARAM_NAME] ), config: { skip_tags: true }
48
+
49
+
data/config/routes.rb CHANGED
@@ -71,6 +71,7 @@ Wco::Engine.routes.draw do
71
71
  resources :office_action_templates
72
72
 
73
73
  post 'office_actions/:id/run', to: 'office_actions#do_run', as: :run_office_action
74
+ get 'office_actions/active', to: 'office_actions#index', defaults: { status: 'active' }, as: :active_office_actions
74
75
  resources :office_actions
75
76
 
76
77
  resources :prices
@@ -95,8 +96,12 @@ Wco::Engine.routes.draw do
95
96
  resources :sitemap_paths, as: :spaths
96
97
  resources :subscriptions
97
98
 
99
+ ## only one resource
98
100
  delete 'tags/remove/:id/from/:resource/:resource_id', to: 'tags#remove_from', as: :remove_tag_from
99
101
  post 'tags/add-to/:resource/:resource_id', to: 'tags#add_to', as: :add_tag_to
102
+ ## many resources
103
+ post 'tags/add-to-many/:resource', to: 'tags#add_to_many', as: :add_tag_to_many
104
+ post 'tags/rm-from-many/:resource', to: 'tags#rm_from_many', as: :rm_tag_from_many
100
105
  resources :tags
101
106
 
102
107
  ## In order to have unsubscribes_url , unsubscribes must be in wco .
data/lib/wco_models.rb CHANGED
@@ -46,7 +46,7 @@ module WcoEmail
46
46
  FIELD_BODY = 'body'
47
47
  FIELD_EXE = 'exe'
48
48
  FIELD_FROM = 'from'
49
- FIELD_LEADSET = 'leadset'
49
+ FIELD_LEADSET = 'leadset_id'
50
50
  FIELD_SUBJECT = 'subject'
51
51
  FIELD_TO = 'to'
52
52
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wco_models
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0.246
4
+ version: 3.1.0.247
5
5
  platform: ruby
6
6
  authors:
7
7
  - Victor Pudeyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-27 00:00:00.000000000 Z
11
+ date: 2026-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ahoy_matey
@@ -404,6 +404,7 @@ files:
404
404
  - app/assets/javascripts/wco/invoices.js
405
405
  - app/assets/javascripts/wco/office_action_templates.js
406
406
  - app/assets/javascripts/wco/shared.js
407
+ - app/assets/javascripts/wco/tags.js
407
408
  - app/assets/stylesheets/dark_theme.css
408
409
  - app/assets/stylesheets/light_theme.css
409
410
  - app/assets/stylesheets/production_theme.scss
@@ -572,8 +573,8 @@ files:
572
573
  - app/views/wco/headlines/new.haml
573
574
  - app/views/wco/invoices/_form.haml
574
575
  - app/views/wco/invoices/_header.haml
575
- - app/views/wco/invoices/_index_list.haml
576
576
  - app/views/wco/invoices/_index_table.haml
577
+ - app/views/wco/invoices/_list.haml
577
578
  - app/views/wco/invoices/edit.haml
578
579
  - app/views/wco/invoices/index.haml
579
580
  - app/views/wco/invoices/new_pdf.haml
@@ -598,6 +599,7 @@ files:
598
599
  - app/views/wco/leads/show.haml
599
600
  - app/views/wco/leadsets/_form.haml
600
601
  - app/views/wco/leadsets/_header.haml
602
+ - app/views/wco/leadsets/_index.haml
601
603
  - app/views/wco/leadsets/_index_chips.haml
602
604
  - app/views/wco/leadsets/_index_list.haml
603
605
  - app/views/wco/leadsets/edit.haml
@@ -698,11 +700,13 @@ files:
698
700
  - app/views/wco/sites/index.haml
699
701
  - app/views/wco/sites/new.haml
700
702
  - app/views/wco/sites/show.haml
703
+ - app/views/wco/tags/_actions.haml
701
704
  - app/views/wco/tags/_form.haml
702
705
  - app/views/wco/tags/_header.haml
703
706
  - app/views/wco/tags/_index.haml
704
707
  - app/views/wco/tags/_index_chips.haml
705
708
  - app/views/wco/tags/_index_table.haml
709
+ - app/views/wco/tags/_index_tree.haml
706
710
  - app/views/wco/tags/_list_chips.haml
707
711
  - app/views/wco/tags/_menuitem.haml
708
712
  - app/views/wco/tags/edit.haml