radiant-shop_packages-extension 0.0.1
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.
- data/.gitignore +2 -0
- data/README +3 -0
- data/Rakefile +138 -0
- data/VERSION +1 -0
- data/app/controllers/.DS_Store +0 -0
- data/app/controllers/admin/shop/packages/packings_controller.rb +90 -0
- data/app/controllers/admin/shop/packages_controller.rb +63 -0
- data/app/datasets/shop_packages.rb +25 -0
- data/app/models/shop_package.rb +79 -0
- data/app/models/shop_packing.rb +22 -0
- data/app/views/admin/shop/packages/edit.html.haml +11 -0
- data/app/views/admin/shop/packages/edit/_foot.html.haml +17 -0
- data/app/views/admin/shop/packages/edit/_form.html.haml +15 -0
- data/app/views/admin/shop/packages/edit/_head.html.haml +4 -0
- data/app/views/admin/shop/packages/edit/_inputs.html.haml +2 -0
- data/app/views/admin/shop/packages/edit/_meta.html.haml +8 -0
- data/app/views/admin/shop/packages/edit/_parts.html.haml +9 -0
- data/app/views/admin/shop/packages/edit/_popups.html.haml +4 -0
- data/app/views/admin/shop/packages/edit/buttons/_browse_products.html.haml +1 -0
- data/app/views/admin/shop/packages/edit/inputs/_name.html.haml +3 -0
- data/app/views/admin/shop/packages/edit/inputs/_price.html.haml +3 -0
- data/app/views/admin/shop/packages/edit/meta/_sku.html.haml +5 -0
- data/app/views/admin/shop/packages/edit/parts/_description.html.haml +1 -0
- data/app/views/admin/shop/packages/edit/parts/_products.html.haml +3 -0
- data/app/views/admin/shop/packages/edit/popups/_browse_products.html.haml +7 -0
- data/app/views/admin/shop/packages/edit/shared/_product.html.haml +8 -0
- data/app/views/admin/shop/packages/index.html.haml +13 -0
- data/app/views/admin/shop/packages/index/_foot.html.haml +8 -0
- data/app/views/admin/shop/packages/index/_head.html.haml +2 -0
- data/app/views/admin/shop/packages/index/_package.html.haml +9 -0
- data/app/views/admin/shop/packages/index/buttons/_new_package.html.haml +1 -0
- data/app/views/admin/shop/packages/new.html.haml +11 -0
- data/app/views/admin/shop/packages/remove.html.haml +12 -0
- data/config/locales/en.yml +11 -0
- data/config/routes.rb +13 -0
- data/cucumber.yml +1 -0
- data/db/migrate/20101015161749_setup_shop_packages.rb +27 -0
- data/lib/shop_packages/interface/packages.rb +34 -0
- data/lib/shop_packages/models/shop_packageable.rb +15 -0
- data/lib/shop_packages/tags/helpers.rb +41 -0
- data/lib/shop_packages/tags/package.rb +94 -0
- data/lib/tasks/shop_packages_extension_tasks.rake +55 -0
- data/public/javascripts/admin/extensions/shop/packages/edit.js +109 -0
- data/public/stylesheets/sass/admin/extensions/shop/packages/edit.sass +162 -0
- data/radiant-shop_packages-extension.gemspec +104 -0
- data/shop_packages_extension.rb +37 -0
- data/spec/controllers/admin/shop/packages/packings_controller_spec.rb +188 -0
- data/spec/controllers/admin/shop/packages_controller_spec.rb +32 -0
- data/spec/datasets/shop_packages.rb +23 -0
- data/spec/lib/shop_packages/tags/package_spec.rb +348 -0
- data/spec/models/shop_package_spec.rb +117 -0
- data/spec/models/shop_packing_spec.rb +67 -0
- data/spec/models/shop_product_spec.rb +25 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +23 -0
- metadata +141 -0
data/.gitignore
ADDED
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |gem|
|
4
|
+
gem.name = "radiant-shop_packages-extension"
|
5
|
+
gem.summary = %Q{Shop Packages Extension for Radiant CMS}
|
6
|
+
gem.description = %Q{RadiantShop: Group up Products into packages, you can assign a price to a package}
|
7
|
+
gem.email = "dk@dirkkelly.com"
|
8
|
+
gem.homepage = "http://github.com/thefrontiergroup/radiant-shop_packages-extension"
|
9
|
+
gem.authors = ["Dirk Kelly"]
|
10
|
+
gem.add_dependency 'radiant-shop-extension'
|
11
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
12
|
+
end
|
13
|
+
Jeweler::GemcutterTasks.new
|
14
|
+
rescue LoadError
|
15
|
+
puts "Jeweler (or a dependency) not available. This is only required if you plan to package shop_packages as a gem."
|
16
|
+
end
|
17
|
+
|
18
|
+
# In rails 1.2, plugins aren't available in the path until they're loaded.
|
19
|
+
# Check to see if the rspec plugin is installed first and require
|
20
|
+
# it if it is. If not, use the gem version.
|
21
|
+
|
22
|
+
# Determine where the RSpec plugin is by loading the boot
|
23
|
+
unless defined? RADIANT_ROOT
|
24
|
+
ENV["RAILS_ENV"] = "test"
|
25
|
+
case
|
26
|
+
when ENV["RADIANT_ENV_FILE"]
|
27
|
+
require File.dirname(ENV["RADIANT_ENV_FILE"]) + "/boot"
|
28
|
+
when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
|
29
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot"
|
30
|
+
else
|
31
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'rake'
|
36
|
+
require 'rake/rdoctask'
|
37
|
+
require 'rake/testtask'
|
38
|
+
|
39
|
+
rspec_base = File.expand_path(RADIANT_ROOT + '/vendor/plugins/rspec/lib')
|
40
|
+
$LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
|
41
|
+
require 'spec/rake/spectask'
|
42
|
+
require 'cucumber'
|
43
|
+
require 'cucumber/rake/task'
|
44
|
+
|
45
|
+
# Cleanup the RADIANT_ROOT constant so specs will load the environment
|
46
|
+
Object.send(:remove_const, :RADIANT_ROOT)
|
47
|
+
|
48
|
+
extension_root = File.expand_path(File.dirname(__FILE__))
|
49
|
+
|
50
|
+
task :default => :spec
|
51
|
+
task :stats => "spec:statsetup"
|
52
|
+
|
53
|
+
desc "Run all specs in spec directory"
|
54
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
55
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
56
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
57
|
+
end
|
58
|
+
|
59
|
+
task :features => 'spec:integration'
|
60
|
+
|
61
|
+
namespace :spec do
|
62
|
+
desc "Run all specs in spec directory with RCov"
|
63
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
64
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
65
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
66
|
+
t.rcov = true
|
67
|
+
t.rcov_opts = ['--exclude', 'spec', '--rails']
|
68
|
+
end
|
69
|
+
|
70
|
+
desc "Print Specdoc for all specs"
|
71
|
+
Spec::Rake::SpecTask.new(:doc) do |t|
|
72
|
+
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
73
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
74
|
+
end
|
75
|
+
|
76
|
+
[:models, :controllers, :views, :helpers].each do |sub|
|
77
|
+
desc "Run the specs under spec/#{sub}"
|
78
|
+
Spec::Rake::SpecTask.new(sub) do |t|
|
79
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
80
|
+
t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
desc "Run the Cucumber features"
|
85
|
+
Cucumber::Rake::Task.new(:integration) do |t|
|
86
|
+
t.fork = true
|
87
|
+
t.cucumber_opts = ['--format', (ENV['CUCUMBER_FORMAT'] || 'pretty')]
|
88
|
+
# t.feature_pattern = "#{extension_root}/features/**/*.feature"
|
89
|
+
t.profile = "default"
|
90
|
+
end
|
91
|
+
|
92
|
+
# Setup specs for stats
|
93
|
+
task :statsetup do
|
94
|
+
require 'code_statistics'
|
95
|
+
::STATS_DIRECTORIES << %w(Model\ specs spec/models)
|
96
|
+
::STATS_DIRECTORIES << %w(View\ specs spec/views)
|
97
|
+
::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers)
|
98
|
+
::STATS_DIRECTORIES << %w(Helper\ specs spec/views)
|
99
|
+
::CodeStatistics::TEST_TYPES << "Model specs"
|
100
|
+
::CodeStatistics::TEST_TYPES << "View specs"
|
101
|
+
::CodeStatistics::TEST_TYPES << "Controller specs"
|
102
|
+
::CodeStatistics::TEST_TYPES << "Helper specs"
|
103
|
+
::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
|
104
|
+
end
|
105
|
+
|
106
|
+
namespace :db do
|
107
|
+
namespace :fixtures do
|
108
|
+
desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y"
|
109
|
+
task :load => :environment do
|
110
|
+
require 'active_record/fixtures'
|
111
|
+
ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
|
112
|
+
(ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
|
113
|
+
Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*'))
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
desc 'Generate documentation for the shop_packages extension.'
|
121
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
122
|
+
rdoc.rdoc_dir = 'rdoc'
|
123
|
+
rdoc.title = 'ShopPackagesExtension'
|
124
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
125
|
+
rdoc.rdoc_files.include('README')
|
126
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
127
|
+
end
|
128
|
+
|
129
|
+
# For extensions that are in transition
|
130
|
+
desc 'Test the shop_packages extension.'
|
131
|
+
Rake::TestTask.new(:test) do |t|
|
132
|
+
t.libs << 'lib'
|
133
|
+
t.pattern = 'test/**/*_test.rb'
|
134
|
+
t.verbose = true
|
135
|
+
end
|
136
|
+
|
137
|
+
# Load any custom rakefiles for extension
|
138
|
+
Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
Binary file
|
@@ -0,0 +1,90 @@
|
|
1
|
+
class Admin::Shop::Packages::PackingsController < Admin::ResourceController
|
2
|
+
model_class ShopPacking
|
3
|
+
|
4
|
+
def sort
|
5
|
+
notice = 'Products successfully sorted.'
|
6
|
+
error = 'Could not sort products.'
|
7
|
+
|
8
|
+
begin
|
9
|
+
@shop_packings = CGI::parse(params[:packings])["package_products[]"]
|
10
|
+
|
11
|
+
@shop_packings.each_with_index do |id, index|
|
12
|
+
ShopPacking.find(id).update_attributes!({ :position, index+1 })
|
13
|
+
end
|
14
|
+
|
15
|
+
respond_to do |format|
|
16
|
+
format.html {
|
17
|
+
redirect_to admin_shop_packages_path
|
18
|
+
}
|
19
|
+
format.js { render :text => notice, :status => :ok }
|
20
|
+
format.json { render :json => { :notice => notice }, :status => :ok }
|
21
|
+
end
|
22
|
+
rescue
|
23
|
+
respond_to do |format|
|
24
|
+
format.html {
|
25
|
+
flash[:error] = error
|
26
|
+
redirect_to admin_shop_packages_path
|
27
|
+
}
|
28
|
+
format.js { render :text => error, :status => :unprocessable_entity }
|
29
|
+
format.json { render :json => { :error => error }, :status => :unprocessable_entity }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def create
|
35
|
+
notice = 'Product successfully attached.'
|
36
|
+
error = 'Could not attach product.'
|
37
|
+
|
38
|
+
begin
|
39
|
+
@shop_packing.package = ShopPackage.find(params[:package_id])
|
40
|
+
@shop_packing.product = ShopProduct.find(params[:product_id])
|
41
|
+
@shop_packing.save!
|
42
|
+
|
43
|
+
respond_to do |format|
|
44
|
+
format.js { render :partial => 'admin/shop/packages/edit/shared/product', :locals => { :product => @shop_packing.product, :packing => @shop_packing } }
|
45
|
+
end
|
46
|
+
rescue
|
47
|
+
respond_to do |format|
|
48
|
+
format.js { render :text => error, :status => :unprocessable_entity }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def update
|
54
|
+
notice = 'Product Quantity successfully updated.'
|
55
|
+
error = 'Could not update Product Quantity.'
|
56
|
+
|
57
|
+
begin
|
58
|
+
@shop_packing.update_attributes!({ :quantity => params[:quantity] })
|
59
|
+
respond_to do |format|
|
60
|
+
format.js { render :text => notice, :status => :ok }
|
61
|
+
end
|
62
|
+
rescue
|
63
|
+
respond_to do |format|
|
64
|
+
format.js { render :text => error, :status => :unprocessable_entity }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
def destroy
|
71
|
+
notice = 'Product successfully removed.'
|
72
|
+
error = 'Could not remove product.'
|
73
|
+
|
74
|
+
begin
|
75
|
+
@shop_product = @shop_packing.product
|
76
|
+
@shop_package = @shop_packing.package
|
77
|
+
|
78
|
+
@shop_packing.destroy
|
79
|
+
|
80
|
+
respond_to do |format|
|
81
|
+
format.js { render :partial => 'admin/shop/packages/edit/shared/product', :locals => { :product => @shop_product } }
|
82
|
+
end
|
83
|
+
rescue
|
84
|
+
respond_to do |format|
|
85
|
+
format.js { render :text => error, :status => :unprocessable_entity }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class Admin::Shop::PackagesController < Admin::ResourceController
|
2
|
+
model_class ShopPackage
|
3
|
+
|
4
|
+
helper :shop
|
5
|
+
|
6
|
+
before_filter :config_global
|
7
|
+
before_filter :config_index, :only => [ :index ]
|
8
|
+
before_filter :config_new, :only => [ :new, :create ]
|
9
|
+
before_filter :config_edit, :only => [ :edit, :update ]
|
10
|
+
before_filter :assets_global
|
11
|
+
before_filter :assets_index, :only => :index
|
12
|
+
before_filter :assets_edit, :only => [ :edit, :update ]
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def config_global
|
17
|
+
@inputs ||= []
|
18
|
+
@meta ||= []
|
19
|
+
@buttons ||= []
|
20
|
+
@parts ||= []
|
21
|
+
@popups ||= []
|
22
|
+
|
23
|
+
@inputs << 'name'
|
24
|
+
@inputs << 'price'
|
25
|
+
end
|
26
|
+
|
27
|
+
def config_index
|
28
|
+
@buttons << 'new_package'
|
29
|
+
end
|
30
|
+
|
31
|
+
def config_new
|
32
|
+
@meta << 'sku'
|
33
|
+
|
34
|
+
@parts << 'description'
|
35
|
+
end
|
36
|
+
|
37
|
+
def config_edit
|
38
|
+
@meta << 'sku'
|
39
|
+
|
40
|
+
@parts << 'description'
|
41
|
+
@parts << 'products'
|
42
|
+
|
43
|
+
@buttons << 'browse_products'
|
44
|
+
|
45
|
+
@popups << 'browse_products'
|
46
|
+
end
|
47
|
+
|
48
|
+
def assets_global
|
49
|
+
end
|
50
|
+
|
51
|
+
def assets_index
|
52
|
+
include_stylesheet 'admin/extensions/shop/index'
|
53
|
+
end
|
54
|
+
|
55
|
+
def assets_edit
|
56
|
+
include_javascript 'admin/dragdrop'
|
57
|
+
include_javascript 'admin/extensions/shop/edit'
|
58
|
+
include_stylesheet 'admin/extensions/shop/edit'
|
59
|
+
include_stylesheet 'admin/extensions/shop/packages/edit'
|
60
|
+
include_javascript 'admin/extensions/shop/packages/edit'
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class ShopPackagesDataset < Dataset::Base
|
2
|
+
|
3
|
+
uses :shop_products
|
4
|
+
|
5
|
+
def load
|
6
|
+
packages = {
|
7
|
+
:bread => [ :soft, :crusty, :warm ]
|
8
|
+
}
|
9
|
+
|
10
|
+
packages.each do |package, products|
|
11
|
+
create_record :shop_package, "all_#{package.to_s}".to_sym,
|
12
|
+
:name => "all #{package.to_s}",
|
13
|
+
:sku => "all_#{package.to_s}",
|
14
|
+
:price => 1 * 10
|
15
|
+
products.each_with_index do |product, i|
|
16
|
+
create_record :shop_packing, "#{product.to_s}_bread".to_s.to_sym,
|
17
|
+
:quantity => 1,
|
18
|
+
:position => 1,
|
19
|
+
:package => shop_packages("all_#{package.to_s}".to_sym),
|
20
|
+
:product => shop_products("#{product.to_s}_bread".to_sym)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
class ShopPackage < ActiveRecord::Base
|
2
|
+
|
3
|
+
belongs_to :created_by, :class_name => 'User'
|
4
|
+
belongs_to :updated_by, :class_name => 'User'
|
5
|
+
|
6
|
+
has_many :packings, :class_name => 'ShopPacking', :foreign_key => :package_id, :dependent => :destroy
|
7
|
+
has_many :products, :class_name => 'ShopProduct', :foreign_key => :product_id, :through => :packings
|
8
|
+
has_many :line_items, :class_name => 'ShopLineItem', :as => :item
|
9
|
+
has_many :orders, :class_name => 'ShopOrder', :through => :line_items
|
10
|
+
|
11
|
+
before_validation :assign_sku
|
12
|
+
|
13
|
+
validates_presence_of :name, :sku
|
14
|
+
|
15
|
+
validates_uniqueness_of :name, :sku
|
16
|
+
|
17
|
+
validates_numericality_of :price, :greater_than => 0.00, :allow_nil => true, :precisions => 2
|
18
|
+
|
19
|
+
# Returns the products not attached to the category
|
20
|
+
def available_products; ShopProduct.all - self.products; end
|
21
|
+
|
22
|
+
# Returns the slug of the first product
|
23
|
+
def slug; packings.first.product.slug; end
|
24
|
+
|
25
|
+
# Returns the url of the first product
|
26
|
+
def url; packings.first.product.url; end
|
27
|
+
|
28
|
+
# Not a valid option, but useful incase a tag is called
|
29
|
+
def category; nil; end
|
30
|
+
|
31
|
+
# Overloads the base to_json to return what we want
|
32
|
+
def to_json(*attrs); super self.class.params; end
|
33
|
+
|
34
|
+
# Returns the aggregate value of the products in the package
|
35
|
+
def value
|
36
|
+
value = 0
|
37
|
+
self.packings.map { |pkg| value += (pkg.product.price.to_f * pkg.quantity) }
|
38
|
+
value
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns the aggregate weight of the products in the package
|
42
|
+
def weight
|
43
|
+
weight = 0
|
44
|
+
self.packings.map { |pkg| weight += (pkg.product.weight.to_f * pkg.quantity) }
|
45
|
+
weight
|
46
|
+
end
|
47
|
+
|
48
|
+
class << self
|
49
|
+
|
50
|
+
# Returns attributes attached to the product
|
51
|
+
def attrs
|
52
|
+
[ :id, :name, :price, :sku, :description, :created_at, :updated_at ]
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns methods with usefuly information
|
56
|
+
def methds
|
57
|
+
[]
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns the objects to include
|
61
|
+
def inclde
|
62
|
+
[ :category, :products ]
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns a custom hash of attributes on the product
|
66
|
+
def params
|
67
|
+
{ :only => self.attrs, :methods => self.methds, :include => self.inclde }
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# Assigns an sku based off the name
|
75
|
+
def assign_sku
|
76
|
+
self.sku = ShopProduct.to_sku(sku.present? ? sku : name)
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class ShopPacking < ActiveRecord::Base
|
2
|
+
|
3
|
+
default_scope :order => 'shop_packings.position ASC'
|
4
|
+
|
5
|
+
belongs_to :package, :class_name => 'ShopPackage', :foreign_key => :package_id
|
6
|
+
belongs_to :product, :class_name => 'ShopProduct', :foreign_key => :product_id
|
7
|
+
|
8
|
+
before_validation :set_quantity
|
9
|
+
|
10
|
+
validates_uniqueness_of :product_id, :scope => :package_id
|
11
|
+
|
12
|
+
def value
|
13
|
+
self.product.price.to_f * self.quantity
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def set_quantity
|
19
|
+
self.quantity = [1,self.quantity].max
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
- @page_title = @shop_package.name
|
2
|
+
|
3
|
+
- render_region :main do |main|
|
4
|
+
- main.head do
|
5
|
+
= render 'admin/shop/packages/edit/head'
|
6
|
+
|
7
|
+
- main.popups do
|
8
|
+
= render 'admin/shop/packages/edit/popups'
|
9
|
+
|
10
|
+
- main.form do
|
11
|
+
= render 'admin/shop/packages/edit/form'
|