wco_models 3.1.0.202 → 3.1.0.203

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 50dbbfc30e0614de28991d3739705b9b632a0c32ee30d3ef601472b9f31c1226
4
- data.tar.gz: 58fa465ee5438bbff6b128b2ef9a3a8553a8741dd7cdfe935a78cf7c6326b1c8
3
+ metadata.gz: 33c64aaf74ef19b78f3ad77b966b57e8c078af95a21960acef1d7a31b2513b43
4
+ data.tar.gz: b2a5a688f5872b582c73cd3b16d4aae7c4ec709bfe46f4136fca93bfd98a2526
5
5
  SHA512:
6
- metadata.gz: 00f3de5db229ff4a4b533867bccb0e1815ad87411fc10bda12cbe83ac29767f89bd7730f8c348f331efc94a0a47b1c02dede82945b54d0a01db8092c3bbe372d
7
- data.tar.gz: 9df79f5ee0b1791686c10a7a0f610541926aa4803162714a03fab2674106e2b58561372145c6833afc4d909e5a37ab2118e1c2a51ce89c25907c578547ac3dde
6
+ metadata.gz: baebd3f21a934e5b8a4ce0adf1e6e615b67d7cfbc77225db40fdcc3b30835ae802132af1c0b2ae954b7db1eaf47a11f303eff9b14838808f4177bc22f03bbb48
7
+ data.tar.gz: 6424f8a5fada86870743887674767e7c4ca39ee7ead9d60130da21954061ed7cdbe9843ad0b4b33653df3e32fd826a1cc4839b8474cf5d77b161f6b33a0724ee
@@ -34,10 +34,20 @@ $enable-important-utilities: false;
34
34
  /* F */
35
35
 
36
36
  .field {
37
+ border-bottom: 1px solid gray;
38
+ border-left: 1px solid gray;
39
+
40
+ // border-top: 2px solid white;
41
+ // border-left: 2px solid white;
42
+
37
43
  display: flex;
44
+
45
+ margin-top: 1em;
38
46
  margin-bottom: 0.5em;
39
47
 
40
- input {
48
+ padding: 0.5em;
49
+
50
+ input[type=text] {
41
51
  flex-grow: 1;
42
52
  }
43
53
 
@@ -22,6 +22,9 @@ textarea {
22
22
  color: blue;
23
23
  }
24
24
 
25
+ .border-left {
26
+ border-left: 1px solid red !important;
27
+ }
25
28
  .border-right {
26
29
  border-right: 1px solid red !important;
27
30
  }
@@ -144,18 +147,19 @@ textarea.monospace {
144
147
 
145
148
  .header {
146
149
  display: flex;
150
+ justify-content: center;
147
151
 
148
152
  > * {
149
153
  padding-right: 0.2em;
150
154
  }
151
155
 
152
- h5.title {
156
+ .title {
153
157
  display: flex;
154
158
  }
155
159
  }
156
160
 
157
161
  .hide, .hidden {
158
- display: none;
162
+ display: none !important;
159
163
  }
160
164
 
161
165
 
@@ -63,6 +63,10 @@ class Wco::LeadsetsController < Wco::ApplicationController
63
63
  @leads = @leadset.leads.page( params[:leads_page] ).per( current_profile.per_page )
64
64
  @subscriptions = @leadset.subscriptions
65
65
  @invoices = @leadset.invoices
66
+
67
+ @prices = @leadset.appliance_tmpls
68
+ ## _TODO: can remove
69
+ @all_prices = Wco::Price.all
66
70
  end
67
71
 
68
72
  def update
@@ -83,8 +87,11 @@ class Wco::LeadsetsController < Wco::ApplicationController
83
87
  private
84
88
 
85
89
  def set_lists
86
- @appliance_tmpls = WcoHosting::ApplianceTmpl.all
90
+ @appliance_tmpls_list = WcoHosting::ApplianceTmpl.all
91
+ @appliance_tmpl_prices_list = Wco::Price.all
92
+ @serverhosts = WcoHosting::Serverhost.all
87
93
  @serverhosts_list = WcoHosting::Serverhost.list
94
+ @tags = Wco::Tag.all
88
95
  @tags_list = Wco::Tag.list
89
96
  @leads_list = Wco::Lead.all.map { |lead| [ lead.email, lead.id ] }
90
97
  @templates_list = WcoEmail::EmailTemplate.all.map { |t| [ t.slug, t.id ] }
@@ -7,6 +7,8 @@ class Wco::PricesController < Wco::ApplicationController
7
7
 
8
8
  @price.interval = nil if !params[:price][:interval].present?
9
9
  @product = params[:price][:product_type].constantize.find @price.product_id
10
+ @price.product = @product
11
+
10
12
  stripe_product = Stripe::Product.retrieve( @product.product_id )
11
13
  price_hash = {
12
14
  product: stripe_product.id,
@@ -17,11 +19,9 @@ class Wco::PricesController < Wco::ApplicationController
17
19
  price_hash[:recurring] = { interval: @price.interval }
18
20
  end
19
21
  stripe_price = Stripe::Price.create( price_hash )
20
- # flash_notice 'Created stripe price.'
21
22
  flash_notice stripe_price
22
-
23
- @price.product = @product
24
23
  @price.price_id = stripe_price[:id]
24
+
25
25
  if @price.save
26
26
  flash_notice @price
27
27
  else
@@ -47,6 +47,13 @@ class Wco::PricesController < Wco::ApplicationController
47
47
  redirect_to request.referrer || root_path
48
48
  end
49
49
 
50
+ def new
51
+ @price = Wco::Price.new
52
+ authorize! :new, @price
53
+
54
+ @products_list = Wco::Product.list + WcoHosting::ApplianceTmpl.list
55
+ end
56
+
50
57
  ## delete and the create, instead
51
58
  # def update
52
59
  # end
@@ -0,0 +1,15 @@
1
+
2
+ From: https://docs.digitalocean.com/reference/api/digitalocean/#tag/Domain-Records/operation/domains_list_records
3
+
4
+ new:
5
+
6
+ POST /v2/domains/$DOMAIN_NAME/records
7
+
8
+ {
9
+ type: 'A',
10
+ name: 'subdomain',
11
+ data: '127.0.0.1',
12
+ ttl: 3600,
13
+ }
14
+
15
+ DELETE /v2/domains/$DOMAIN_NAME/records/$DOMAIN_RECORD_ID
@@ -34,13 +34,13 @@ class Wco::Leadset
34
34
  validates :email, uniqueness: { allow_nil: true } # presence: true
35
35
 
36
36
 
37
- has_many :appliances, class_name: '::WcoHosting::Appliance', inverse_of: :leadset
38
- has_many :appliance_tmpl_prices, class_name: 'Wco::Price'
37
+ has_many :appliances, class_name: '::WcoHosting::Appliance', inverse_of: :leadset
38
+ has_many :appliance_tmpls, class_name: 'Wco::Price', inverse_of: :leadset
39
39
 
40
40
  has_many :environments, class_name: '::WcoHosting::Environment', inverse_of: :leadset
41
41
  has_many :invoices, class_name: 'Wco::Invoice'
42
42
  has_many :leads, class_name: 'Wco::Lead'
43
- has_many :subdomains, class_name: 'WcoHosting::Subdomain'
43
+ # has_many :subdomains, class_name: 'WcoHosting::Subdomain'
44
44
 
45
45
  has_many :profiles, class_name: 'Wco::Profile', inverse_of: :leadset
46
46
  has_many :subscriptions, class_name: 'Wco::Subscription', inverse_of: :leadset
@@ -0,0 +1,15 @@
1
+
2
+ class Wco::LeadsetApplianceTmpl
3
+ include Mongoid::Document
4
+ include Mongoid::Timestamps
5
+ include Mongoid::Paranoia
6
+ store_in collection: 'wco_leadset_appliance_tmpls'
7
+
8
+ belongs_to :leadset, class_name: 'Wco::Leadset'
9
+ belongs_to :appliance_tmpl, class_name: 'WcoHosting::ApplianceTmpl'
10
+ has_one :price, class_name: 'Wco::Price'
11
+
12
+
13
+ end
14
+
15
+
@@ -13,10 +13,15 @@ class Wco::Log
13
13
 
14
14
  has_and_belongs_to_many :tags
15
15
 
16
+ def self.puts message, obj: nil
17
+ create( message: message.to_s, obj: obj )
18
+ p message
19
+ end
20
+
16
21
  def self.puts! message, label, obj: nil
17
- create( message: message, label: label, obj: obj )
18
- puts "+++ +++ #{label}:"
19
- puts message.inspect
22
+ create( message: message.to_s, label: label.to_s, obj: obj )
23
+ p "+++ +++ #{label}:"
24
+ p message.inspect
20
25
  end
21
26
 
22
27
  def to_s
@@ -8,7 +8,7 @@ class Wco::Price
8
8
  ## Wco::Product, WcoHosting::ApplianceTmpl
9
9
  belongs_to :product, polymorphic: true
10
10
 
11
- belongs_to :appliance_tmpl_leadset, class_name: 'Wco::Leadset', optional: true
11
+ belongs_to :leadset, class_name: 'Wco::Leadset'
12
12
 
13
13
  has_many :subscriptions, class_name: 'Wco::Subscription', inverse_of: :price, foreign_key: :wco_price_id
14
14
 
@@ -11,17 +11,18 @@ class Wco::Profile
11
11
 
12
12
 
13
13
  field :per_page, type: :integer, default: 25
14
+ field :show_n_thumbs, type: :integer, default: 8
15
+
14
16
 
15
17
  field :schwab_access_token, type: :string
16
18
  field :schwab_refresh_token, type: :string
17
19
  field :schwab_id_token, type: :string
18
20
 
19
- field :show_n_thumbs, type: :integer, default: 8
20
21
 
21
22
  has_many :reports, class_name: 'Wco::Report'
22
23
  has_many :stocks, class_name: 'Iro::Stock'
23
24
 
24
- belongs_to :leadset, class_name: 'Wco::Leadset', inverse_of: :profile, optional: true
25
+ belongs_to :leadset, class_name: 'Wco::Leadset', inverse_of: :profile
25
26
  has_many :newsitems, class_name: 'Wco::Newsitem'
26
27
  has_and_belongs_to_many :shared_galleries, class_name: 'Wco::Gallery', inverse_of: :shared_profiles
27
28
 
@@ -6,23 +6,23 @@ class WcoHosting::Appliance
6
6
  include Wco::Utils
7
7
  store_in collection: 'wco_appliances'
8
8
 
9
- has_many :logs, as: :obj, class_name: 'Wco::Log'
10
- has_many :files, class_name: 'WcoHosting::File'
9
+ # field :slug, type: String
10
+ def slug
11
+ "#{subdomain}_#{domain.name.gsub('.', '_')}"
12
+ end
13
+
14
+ has_many :logs, class_name: 'Wco::Log', inverse_of: :obj
15
+ has_many :files, class_name: 'WcoHosting::File'
11
16
 
12
17
  field :rc_json, type: Object, default: '{}'
13
18
  def rc
14
19
  OpenStruct.new JSON.parse rc_json
15
20
  end
16
21
 
17
- belongs_to :leadset, class_name: 'Wco::Leadset', inverse_of: :appliances
18
- belongs_to :subscription, class_name: 'Wco::Subscription' # , inverse_of: :appliance
19
-
20
- # field :service_name
21
- # before_validation :set_service_name, on: :create, unless: ->{ service_name }
22
- # def set_service_name
23
- # self[:service_name] = host.gsub(".", "_")
24
- # end
22
+ field :port
25
23
 
24
+ belongs_to :leadset, class_name: 'Wco::Leadset', inverse_of: :appliances
25
+ belongs_to :subscription, class_name: 'Wco::Subscription', optional: true # , inverse_of: :appliance
26
26
 
27
27
  belongs_to :environment, class_name: 'WcoHosting::Environment', inverse_of: :appliances, optional: true
28
28
  def environment_name
@@ -45,13 +45,15 @@ class WcoHosting::Appliance
45
45
 
46
46
  belongs_to :serverhost, class_name: 'WcoHosting::Serverhost', optional: true
47
47
 
48
- field :port
49
48
 
50
49
  STATE_PENDING = 'pending'
51
50
  STATE_LIVE = 'live'
52
51
  STATE_TERMINATED = 'terminated'
53
52
  field :state, default: STATE_PENDING
54
53
 
54
+ field :stdout, default: ''
55
+ field :stderr, default: ''
56
+
55
57
  def to_s
56
58
  appliance_tmpl # kind
57
59
  end
@@ -76,8 +76,9 @@ class WcoHosting::ApplianceTmpl
76
76
  end
77
77
 
78
78
  has_many :appliances, class_name: 'WcoHosting::Appliance'
79
+ has_many :leadsets, class_name: 'Wco::LeadsetApplianceTmpl', inverse_of: :appliance_tmpl
79
80
  has_many :subscriptions, as: :product, class_name: 'Wco::Subscription'
80
- has_many :prices, as: :product, class_name: 'Wco::Price'
81
+ has_many :prices, as: :product, class_name: 'Wco::Price'
81
82
  has_and_belongs_to_many :task_tmpls, class_name: 'WcoHosting::TaskTmpl'
82
83
 
83
84
  field :product_id # stripe
@@ -8,7 +8,7 @@ class WcoHosting::Domain
8
8
  field :name
9
9
  validates :name, presence: true, uniqueness: true
10
10
 
11
- has_many :subdomains, class_name: 'WcoHosting::Subdomain'
11
+ has_many :appliances, class_name: 'WcoHosting::Appliance'
12
12
 
13
13
  STATUS_ACTIVE = 'active'
14
14
  STATUS_INACTIVE = 'inactive'
@@ -27,34 +27,31 @@ class WcoHosting::Serverhost
27
27
  # field :ssh_username
28
28
  # field :ssh_key
29
29
 
30
+ field :do_id, type: :string
31
+
30
32
  has_many :appliances, class_name: 'WcoHosting::Appliance'
31
33
  has_many :files, class_name: 'WcoHosting::File'
32
34
 
33
- def create_appliance app
34
- # puts! app, 'Serverhost#create_appliance'
35
-
36
- create_subdomain( app )
37
- create_volume( app )
38
- add_docker_service( app )
39
- add_nginx_site( app )
40
- # load_database( app )
35
+ def self.do_list_keys
36
+ out = HTTParty.get( "https://api.digitalocean.com/v2/account/keys",
37
+ headers: { 'Authorization' => "Bearer #{DO_READER_TOKEN}" },
38
+ :debug_output => $stdout,
39
+ );
40
+ end
41
41
 
42
- update({ next_port: app.serverhost.next_port + 1 })
42
+ def self.list
43
+ [[nil,nil]] + all.map { |s| [s.name, s.id] }
44
+ # all.map { |s| [s.name, s.id] }
43
45
  end
44
46
 
45
- def create_subdomain app
47
+ def add_docker_service app
46
48
  @obj = app
47
- Wco::Log.puts! @obj, '#create_subdomain...', obj: @obj
48
-
49
- client = DropletKit::Client.new(access_token: DO_TOKEN_1)
50
- record = DropletKit::DomainRecord.new(
51
- type: 'A',
52
- name: app.subdomain,
53
- data: app.serverhost.public_ip,
54
- )
55
- client.domain_records.create(record, for_domain: app.domain )
56
-
57
- Wco::Log.puts! record, 'created subdomain?', obj: @obj
49
+ cmd =<<~AOL
50
+ cd /Users/piousbox/projects/ansible
51
+ . zenv/bin/activate
52
+ ansible-playbook -i inventory/do.yml --limit #{self.name} playbooks/hosted-packagedapp.yml --extra-vars '{"appliance_slug": "#{app.slug}", "codebase_zip": "#{app.tmpl.volume_zip_url}", "next_port": "#{self.next_port}"}'
53
+ AOL
54
+ do_exec cmd
58
55
  end
59
56
 
60
57
  def add_nginx_site app
@@ -79,87 +76,69 @@ class WcoHosting::Serverhost
79
76
  do_exec cmd
80
77
  end
81
78
 
82
- def add_docker_service app
83
- @obj = app
84
- Wco::Log.puts! nil, '#add_docker_service', obj: @obj
85
-
86
- ac = ActionController::Base.new
87
- ac.instance_variable_set( :@app, app )
88
- ac.instance_variable_set( :@workdir, WORKDIR )
89
- # rendered_str = ac.render_to_string("wco_hosting/docker-compose/dc-#{app.tmpl.kind}")
90
- rendered_str = ac.render_to_string("wco_hosting/docker-compose/dc-any")
91
- Wco::Log.puts! rendered_str, 'add_docker_service rendered_str', obj: @obj
92
-
93
- file = Tempfile.new('prefix')
94
- file.write rendered_str
95
- file.close
96
- # puts! file.path, 'file.path'
97
-
98
- cmd = "scp #{file.path} #{ssh_host}:#{WORKDIR}/dc-#{app.service_name}.yml "
99
- do_exec cmd
100
-
101
- cmd = "ssh #{ssh_host} 'cd #{WORKDIR} ; \
102
- docker compose -f dc-#{app.service_name}.yml up -d #{app.service_name} ; \
103
- echo ok #add_docker_service ' "
104
- do_exec cmd
79
+ =begin
80
+ def create_appliance app
81
+ # puts! app, 'Serverhost#create_appliance'
82
+ create_subdomain( app )
83
+ create_volume( app )
84
+ add_docker_service( app )
85
+ add_nginx_site( app )
86
+ # load_database( app )
105
87
  end
88
+ =end
106
89
 
107
- def create_wordpress_volume app
90
+ def create_subdomain app
108
91
  @obj = app
109
-
110
- ac = ActionController::Base.new
111
- ac.instance_variable_set( :@app, app )
112
- ac.instance_variable_set( :@workdir, WORKDIR )
113
- rendered_str = ac.render_to_string("wco_hosting/scripts/create_volume")
114
- Wco::Log.puts! rendered_str, 'create_volume rendered_str', obj: @obj
115
-
116
- file = Tempfile.new('prefix')
117
- file.write rendered_str
118
- file.close
119
- # puts! file.path, 'file.path'
120
-
121
- cmd = "scp #{file.path} #{ssh_host}:#{WORKDIR}/scripts/create_volume"
122
- do_exec cmd
123
-
124
- cmd = "ssh #{ssh_host} 'chmod a+x #{WORKDIR}/scripts/create_volume ; \
125
- #{WORKDIR}/wco_hosting/scripts/create_volume ' "
126
- do_exec cmd
92
+ Wco::Log.puts! @obj, 'Creating subdomain...', obj: @obj
93
+ client = DropletKit::Client.new(access_token: DO_TOKEN_1)
94
+ record = DropletKit::DomainRecord.new(
95
+ type: 'A',
96
+ name: app.subdomain,
97
+ data: app.serverhost.public_ip,
98
+ )
99
+ client.domain_records.create(record, for_domain: app.domain )
100
+ Wco::Log.puts! record, 'Created subdomain.', obj: @obj
101
+ # WcoHosting::Subdomain.create!( domain: app.domain, name: app.subdomain )
127
102
  end
128
103
 
129
- def create_volume app
130
- @obj = app
131
- Wco::Log.puts! app.service_name, 'Serverhost#create_volume', obj: @obj
132
-
133
- ac = ActionController::Base.new
134
- ac.instance_variable_set( :@app, app )
135
- ac.instance_variable_set( :@workdir, WORKDIR )
136
- rendered_str = ac.render_to_string("wco_hosting/scripts/create_volume")
137
- Wco::Log.puts! rendered_str, 'create_volume rendered_str', obj: @obj
138
-
139
- file = Tempfile.new('prefix')
140
- file.write rendered_str
141
- file.close
142
- # puts! file.path, 'file.path'
143
-
144
- cmd = "scp #{file.path} #{ssh_host}:#{WORKDIR}/scripts/create_volume"
145
- do_exec( cmd )
146
-
147
- cmd = "ssh #{ssh_host} 'chmod a+x #{WORKDIR}/scripts/create_volume ; #{WORKDIR}/scripts/create_volume ' "
148
- do_exec( cmd )
104
+ def do_create_server!
105
+ out = HTTParty.post( "https://api.digitalocean.com/v2/droplets", body: {
106
+ name: name,
107
+ size: "s-1vcpu-2gb",
108
+ region: "sfo2",
109
+ image: "ubuntu-22-04-x64",
110
+ ssh_keys: [ 40460634 ], # 2np, "71:9a:7e:9a:54:55:af:d0:16:47:5c:57:a8:c3:f0:2b"
111
+ vpc_uuid: "b9f748b6-4649-4506-a457-b70d2b57b313",
112
+ }.to_json,
113
+ headers: { 'Content-Type' => 'application/json',
114
+ 'Authorization' => "Bearer #{DO_DROPLET_TOKEN}" },
115
+ :debug_output => $stdout,
116
+ );
117
+ puts! out, '#create_do_server()'
118
+ out = out.parsed_response
119
+ self.do_id = out['droplet']['id']
120
+ self.save
121
+
122
+ do_project_id = '4dad5c55-1a11-4c2b-9e14-1cd69513d940' # WcoHosting DO project id
123
+ out = HTTParty.post( "https://api.digitalocean.com/v2/projects/#{do_project_id}/resources", body: {
124
+ resources: [ "do:droplet:#{out['droplet']['id']}" ],
125
+ }.to_json,
126
+ headers: { 'Content-Type' => 'application/json',
127
+ 'Authorization' => "Bearer #{DO_DROPLET_TOKEN}" },
128
+ :debug_output => $stdout,
129
+ );
130
+ puts! out, 'assign droplet to a project'
149
131
  end
150
132
 
151
133
  def do_exec cmd
152
134
  Wco::Log.puts! cmd, '#do_exec', obj: @obj
153
-
154
- stdout, stderr, status = Open3.capture3(cmd)
155
- status = status.to_s.split.last.to_i
156
- Wco::Log.puts! stdout, 'stdout', obj: @obj
157
- Wco::Log.puts! stderr, 'stderr', obj: @obj
158
- Wco::Log.puts! status, 'status', obj: @obj
135
+ IO.popen(cmd).each do |line|
136
+ Wco::Log.puts line, obj: @obj
137
+ end
159
138
  end
160
139
 
161
- def self.list
162
- [[nil,nil]] + all.map { |s| [s.name, s.id] }
163
- # all.map { |s| [s.name, s.id] }
140
+ def to_s
141
+ "<Serverhost name=#{name} next_port=#{next_port} ssh_host=#{ssh_host} />"
164
142
  end
143
+
165
144
  end
@@ -1,4 +1,8 @@
1
1
 
2
+ ##
3
+ ## Subdomain is not an object b/c domain.appliances is like the same thing.
4
+ ##
5
+
2
6
  class WcoHosting::Subdomain
3
7
  include Mongoid::Document
4
8
  include Mongoid::Timestamps
@@ -3,32 +3,52 @@
3
3
 
4
4
  = form_for leadset, :as => :leadset, :url => url do |f|
5
5
 
6
- .input-field
6
+ .field
7
7
  = f.label :company_url
8
8
  = f.text_field :company_url
9
9
 
10
- .input-field
10
+ .field
11
11
  = f.label :email
12
12
  = f.text_field :email
13
13
  .field
14
14
  %label mangle_subject
15
15
  = f.check_box :mangle_subject
16
16
 
17
- .input-field
17
+ .field
18
18
  = f.label :next_invoice_number
19
19
  = f.text_field :next_invoice_number
20
20
 
21
+ .field
22
+ %label tags
23
+ .d-flex.flex-wrap
24
+ - @tags.each do |tag|
25
+ .descr
26
+ = check_box_tag 'leadset[tag_ids][]', tag.id, leadset.tags.include?(tag)
27
+ %label= tag
28
+
29
+ .header
30
+ %h5.title Hosting
31
+
21
32
  .field
22
33
  %label serverhosts
23
- = f.select :serverhost_ids, options_for_select(@serverhosts_list, selected: leadset.serverhost_ids), {}, { multiple: true }
34
+ .d-flex.flex-wrap
35
+ - @serverhosts.each do |opt|
36
+ .descr
37
+ = check_box_tag 'leadset[serverhost_ids][]', opt.id, leadset.serverhost_ids.include?(opt.id)
38
+ %label= opt.name
24
39
 
25
- -# .input-field
26
- -# %label Stripe customer_id
27
- -# = f.text_field :customer_id
40
+ .field
41
+ %label Stripe customer_id
42
+ = f.text_field :customer_id
28
43
 
29
44
  .field
30
- %label tags
31
- = f.select :tag_ids, options_for_select(@tags_list, selected: leadset.tag_ids), {}, { multiple: true }
45
+ %label appliance tmpl prices
46
+ .d-flex.flex-wrap
47
+ - @appliance_tmpl_prices_list.each do |price|
48
+ .descr
49
+ = check_box_tag 'leadset[appliance_tmpl_ids][]', price.id, leadset.appliance_tmpls.include?(price)
50
+ %label= price.product.name
51
+
32
52
 
33
53
  .actions
34
54
  = f.submit
@@ -5,86 +5,90 @@
5
5
  %h2.title
6
6
  Leadset `#{@leadset.company_url}`
7
7
  = link_to '[~]', edit_leadset_path( @leadset )
8
+ .row
9
+ .col-6.border-right
10
+ %ul
11
+ %li <b>Email:</b> #{@leadset.email}
12
+ %li <b>company_url</b>: #{@leadset.company_url}
13
+ %li <b>customer_id</b>: #{@leadset.customer_id}
14
+ -# tags
15
+ .col-6
16
+ Tags:
17
+ = render '/wco/tags/index_chips', tags: @leadset.tags
8
18
 
9
- %ul
10
- %li <b>Email:</b> #{@leadset.email}
11
- %li <b>company_url</b>: #{@leadset.company_url}
12
- %li <b>customer_id</b>: #{@leadset.customer_id}
13
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;
14
26
 
15
- .row
16
- .col-md-6
17
- %h5 Appliance Tmpls (prices)
18
- %table.bordered.data-table
19
- %thead
20
- %td &nbsp;
21
- %td Appliance Tmpl
22
- %td Amount Cents
23
- %td Interval
24
- - @appliance_tmpls.each do |tmpl|
25
- %tr
26
- - price = @leadset.appliance_tmpl_prices.where( product: tmpl ).first
27
- - price ||= Wco::Price.new({ product: tmpl, appliance_tmpl_leadset: @leadset })
28
- - url = price.new_record? ? wco.prices_path : wco.price_path(price)
29
- - if !price.new_record?
30
- %td
31
- = button_to 'x', wco.price_path(price), method: :delete, data: { confirm: 'Are you sure?' }
32
- - else
33
- %td
34
- = form_for price, url: url do |f|
35
- = hidden_field_tag 'price[product_id]', price.product_id
36
- = hidden_field_tag 'price[product_type]', price.product_type
37
- = hidden_field_tag 'price[appliance_tmpl_leadset_id]', @leadset.id
38
- %td
39
- %b= tmpl
40
- %td
41
- = f.number_field :amount_cents
42
- %td
43
- = f.select :interval, options_for_select( Wco::Price::INTERVALS, selected: price.interval ), class: 'select2'
44
- = f.submit '>'
45
- &nbsp;
46
- %br
47
- .gray= price.price_id || 'nil'
27
+ %hr
28
+ .row
29
+ -# appliance templates (prices)
30
+ .col-md-6.border-right
31
+ .header
32
+ %h5.title Appliance Tmpls (prices) (#{@leadset.appliance_tmpls.length})
33
+ = render 'wco/prices/form_table'
48
34
 
49
- .col-md-6
50
- %h5.collapse-expand#subscriptionsList
51
- Subscriptions (#{@subscriptions.length})
52
- = link_to '[+wco]', new_subscription_path({ customer_id: @leadset.customer_id })
53
- %table.bordered
54
- %thead
55
- %tr
56
- %th Name
57
- %th Price
58
- - @subscriptions.each do |i|
59
- %tr
60
- %td= i.product.name
61
- %td= i.price
62
- %hr
35
+ -# environments
36
+ .col-md-6
37
+ - if defined?( wco_hosting )
38
+ .header
39
+ %h5.title Environments (#{@leadset.environments.length}) #{link_to '[+]', wco_hosting.new_environment_path({ leadset_id: @leadset.id }) }
40
+ %ul
41
+ - @leadset.environments.each do |env|
42
+ %li= env
43
+ %hr
44
+ .row
45
+ -# appliances
46
+ .col-md-6.border-right
47
+ .header
48
+ %h5.title Appliances (#{@leadset.appliances.length})
63
49
 
64
- .row.maxwidth
65
- .col-md-6
66
- %h5.collapse-expand#invoicesList
67
- Invoices (#{@leadset.invoices.length})
68
- = link_to '[+stripe]', new_invoice_stripe_path({ leadset_id: @leadset.id })
69
- = link_to '[+pdf]', new_invoice_pdf_path({ leadset_id: @leadset.id })
50
+ -# subscriptions
51
+ .col-md-6
52
+ .header
53
+ %h5.title.collapse-expand#subscriptionsList
54
+ Subscriptions (#{@subscriptions.length})
55
+ = link_to '[+wco]', new_subscription_path({ customer_id: @leadset.customer_id })
56
+ %table.bordered
57
+ %thead
58
+ %tr
59
+ %th Name
60
+ %th Price
61
+ - @subscriptions.each do |i|
62
+ %tr
63
+ %td= i.product.name
64
+ %td= i.price
70
65
 
71
- = render '/wco/invoices/index_list', invoices: @invoices
66
+ %hr
67
+ .row
68
+ -# invoices
69
+ .col-md-12
70
+ .header
71
+ %h5.title.collapse-expand#invoicesList
72
+ Invoices (#{@leadset.invoices.length})
73
+ = link_to '[+stripe]', new_invoice_stripe_path({ leadset_id: @leadset.id })
74
+ = link_to '[+pdf]', new_invoice_pdf_path({ leadset_id: @leadset.id })
72
75
 
73
- %p Next invoice number: #{@leadset.next_invoice_number}
76
+ = render '/wco/invoices/index_list', invoices: @invoices
74
77
 
75
- = form_tag create_monthly_invoice_for_leadset_path({ leadset_id: @leadset.id }) do
76
- %label Create invoice for month
77
- = text_field_tag 'month_on', nil, placeholder: 'YYYY-MM-DD'
78
- %label (replace ?)
79
- = check_box_tag 'replace'
80
- = submit_tag 'Go >', data: { confirm: 'Are you sure?' }
81
- %hr
78
+ %p Next invoice number: #{@leadset.next_invoice_number}
82
79
 
83
- .row
84
- .col-6
85
- %h5.collapse-expand#leads Leads
86
- = render '/wco/leads/index', leads: @leads
87
- .col-6
88
- Tags:
89
- = render '/wco/tags/index_chips', tags: @leadset.tags
80
+ = form_tag create_monthly_invoice_for_leadset_path({ leadset_id: @leadset.id }) do
81
+ %label Create invoice for month
82
+ = text_field_tag 'month_on', nil, placeholder: 'YYYY-MM-DD'
83
+ %label (replace ?)
84
+ = check_box_tag 'replace'
85
+ = submit_tag 'Go >', data: { confirm: 'Are you sure?' }
86
+
87
+ %hr
88
+ .row
89
+ -# leads
90
+ .col-12
91
+ .header
92
+ %h5.title.collapse-expand#leads Leads
93
+ = render '/wco/leads/index', leads: @leads
90
94
 
@@ -0,0 +1,18 @@
1
+
2
+ -# - url = price.new_record? ? prices_path : price_path( price )
3
+ .prices--form
4
+
5
+ = form_for price, as: :price, html: { class: 'flex-row' } do |f|
6
+ = hidden_field_tag 'price[product_id]', price.product_id
7
+ = hidden_field_tag 'price[product_type]', price.product_type
8
+
9
+ .field
10
+ %label amount cents
11
+ = f.number_field :amount_cents
12
+
13
+ .field
14
+ %label interval
15
+ = f.select :interval, options_for_select( Wco::Price::INTERVALS, selected: price.interval ), class: 'select2'
16
+
17
+ .actions
18
+ = f.submit '>'
@@ -0,0 +1,50 @@
1
+
2
+ .prices--form-table
3
+
4
+ -# %label New price (app tmpl leadset):
5
+
6
+ %table.bordered
7
+ %thead
8
+ %tr
9
+ %th
10
+ %th tmpl
11
+ %th price
12
+ %th interval
13
+ %th
14
+ %tbody
15
+ - @appliance_tmpls_list.each do |tmpl|
16
+ - price = @leadset.appliance_tmpls.where( product: tmpl ).first
17
+ - price ||= Wco::Price.new({ product: tmpl, leadset: @leadset })
18
+ - url = price.new_record? ? wco.prices_path : wco.price_path(price)
19
+ - if !price.new_record?
20
+ %tr
21
+ %td
22
+ %td
23
+ %b= tmpl
24
+ %td
25
+ = price.amount_cents
26
+ cents
27
+ %td
28
+ = price.interval
29
+ %td
30
+ = button_to 'x', wco.price_path(price), method: :delete, data: { confirm: 'Are you sure?' }
31
+ - else
32
+ = form_for price, url: url do |f|
33
+ %tr
34
+ %td
35
+ .d-flex.flex-column.hide
36
+ product_id
37
+ = text_field_tag 'price[product_id]', price.product_id
38
+ product_type
39
+ = text_field_tag 'price[product_type]', price.product_type
40
+ leadset_id
41
+ = text_field_tag 'price[leadset_id]', @leadset.id
42
+ %td
43
+ %b= tmpl
44
+ %td
45
+ = f.number_field :amount_cents
46
+ %td
47
+ = f.select :interval, options_for_select( Wco::Price::INTERVALS, selected: price.interval ), class: 'select2'
48
+ %td
49
+ = f.submit '>'
50
+
@@ -0,0 +1,22 @@
1
+
2
+ .maxwidth
3
+ .header
4
+ %h5.title New Price
5
+
6
+ - price = @price
7
+ = form_for price, as: :price do |f|
8
+ .field
9
+ %label Product
10
+ = f.select :product_id, options_for_select(@products_list, selected: price.product_id)
11
+
12
+ .field
13
+ %label amount cents
14
+ = f.number_field :amount_cents
15
+
16
+ .field
17
+ %label interval
18
+ = f.select :interval, options_for_select( Wco::Price::INTERVALS, selected: price.interval ), class: 'select2'
19
+
20
+ .actions
21
+ = f.submit 'Create'
22
+
@@ -1,5 +1,5 @@
1
1
 
2
- .products-index.maxwidth
2
+ .products-index.padded
3
3
 
4
4
  .a
5
5
  %h5 New Product:
@@ -19,5 +19,11 @@
19
19
  %label schwab_id_token
20
20
  = f.text_field :schwab_id_token
21
21
 
22
+ %hr
23
+
24
+ .field
25
+ %label leadset
26
+ = f.select :leadset, options_for_select(Wco::Leadset.list, selected: profile.leadset )
27
+
22
28
  .actions
23
29
  = f.submit 'Go'
@@ -1,6 +1,6 @@
1
1
 
2
2
  .profiles-edit.maxwidth
3
3
  .header
4
- .title edit profile
4
+ %h3.title edit profile
5
5
 
6
6
  = render 'form', profile: @profile
@@ -8,3 +8,4 @@
8
8
  %li
9
9
  = link_to '[~]', edit_profile_path(profile)
10
10
  = profile.email
11
+ <b>Leadset:</b> #{link_to profile.leadset, leadset_path(profile.leadset)}
@@ -0,0 +1,15 @@
1
+
2
+ .serverhosts--list
3
+ - if !defined?( wco_hosting )
4
+ .header
5
+ %h5.title wco_hosting is not included
6
+ - else
7
+ .header
8
+ %h5.title
9
+ Serverhosts for `#{@leadset}`
10
+ = link_to '[+]', wco_hosting.new_serverhost_path({ leadset_id: @leadset.id })
11
+ %ul
12
+ - serverhosts.each do |s|
13
+ %li
14
+ = link_to '[~]', wco_hosting.edit_serverhost_path(s)
15
+ = s
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.202
4
+ version: 3.1.0.203
5
5
  platform: ruby
6
6
  authors:
7
7
  - Victor Pudeyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-17 00:00:00.000000000 Z
11
+ date: 2025-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ahoy_matey
@@ -459,6 +459,7 @@ files:
459
459
  - app/models/ability.rb
460
460
  - app/models/ahoy/event.rb
461
461
  - app/models/ahoy/visit.rb
462
+ - app/models/do/dns_record.rb-txt
462
463
  - app/models/iro/alert.rb
463
464
  - app/models/iro/datapoint.rb
464
465
  - app/models/iro/date.rb
@@ -475,6 +476,7 @@ files:
475
476
  - app/models/wco/invoice.rb
476
477
  - app/models/wco/lead.rb
477
478
  - app/models/wco/leadset.rb
479
+ - app/models/wco/leadset_appliance_tmpl.rb
478
480
  - app/models/wco/log.rb
479
481
  - app/models/wco/newsitem.rb
480
482
  - app/models/wco/obfuscated_redirect.rb
@@ -516,7 +518,7 @@ files:
516
518
  - app/models/wco_hosting/file.rb
517
519
  - app/models/wco_hosting/runner.rb
518
520
  - app/models/wco_hosting/serverhost.rb
519
- - app/models/wco_hosting/subdomain.rb
521
+ - app/models/wco_hosting/subdomain.rb-bk
520
522
  - app/models/wco_hosting/task.rb
521
523
  - app/models/wco_hosting/task_tmpl.rb
522
524
  - app/views/layouts/wco/application.haml
@@ -618,6 +620,9 @@ files:
618
620
  - app/views/wco/photos/show.haml
619
621
  - app/views/wco/photos/without_gallery.haml
620
622
  - app/views/wco/prices/_form.haml
623
+ - app/views/wco/prices/_form_mini.haml
624
+ - app/views/wco/prices/_form_table.haml
625
+ - app/views/wco/prices/new.haml
621
626
  - app/views/wco/products/_form.haml
622
627
  - app/views/wco/products/_header.haml
623
628
  - app/views/wco/products/_index.haml
@@ -714,6 +719,7 @@ files:
714
719
  - app/views/wco_hosting/ecs_task_definitions/hw1.json
715
720
  - app/views/wco_hosting/scripts/create_volume.erb
716
721
  - app/views/wco_hosting/scripts/nginx_site.conf.erb
722
+ - app/views/wco_hosting/serverhosts/_list.haml
717
723
  - config/initializers/00_s3.rb
718
724
  - config/initializers/00_s3.rb-example
719
725
  - config/initializers/08_integrations.rb