rfcommerce_dash 0.0.3
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/Gemfile +35 -0
- data/LICENSE +26 -0
- data/README.md +14 -0
- data/app/controllers/admin/overview_controller.rb +152 -0
- data/app/views/admin/overview/index.html.erb +174 -0
- data/config/routes.rb +4 -0
- data/lib/spree_dash.rb +6 -0
- data/lib/tasks/install.rake +24 -0
- data/public/javascripts/dashboard.js +143 -0
- data/public/javascripts/jqPlot/excanvas.min.js +1 -0
- data/public/javascripts/jqPlot/jquery.jqplot.min.js +14 -0
- data/public/javascripts/jqPlot/plugins/jqplot.canvasAxisLabelRenderer.min.js +14 -0
- data/public/javascripts/jqPlot/plugins/jqplot.canvasAxisTickRenderer.min.js +14 -0
- data/public/javascripts/jqPlot/plugins/jqplot.canvasTextRenderer.min.js +14 -0
- data/public/javascripts/jqPlot/plugins/jqplot.categoryAxisRenderer.min.js +14 -0
- data/public/javascripts/jqPlot/plugins/jqplot.dateAxisRenderer.min.js +14 -0
- data/public/javascripts/jqPlot/plugins/jqplot.highlighter.min.js +14 -0
- data/public/javascripts/jqPlot/plugins/jqplot.pieRenderer.min.js +14 -0
- data/public/stylesheets/admin/dashboard.css +143 -0
- metadata +141 -0
data/Gemfile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
source 'http://rubygems.org'
|
2
|
+
|
3
|
+
group :test do
|
4
|
+
gem 'rspec-rails', '= 2.5.0'
|
5
|
+
gem 'factory_girl', '= 1.3.3'
|
6
|
+
gem 'factory_girl_rails', '= 1.0.1'
|
7
|
+
gem 'simplecov'
|
8
|
+
gem 'shoulda'
|
9
|
+
gem 'faker'
|
10
|
+
if RUBY_VERSION < "1.9"
|
11
|
+
gem "ruby-debug"
|
12
|
+
else
|
13
|
+
gem "ruby-debug19"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
group :cucumber do
|
18
|
+
gem 'cucumber-rails'
|
19
|
+
gem 'database_cleaner', '= 0.6.7'
|
20
|
+
gem 'nokogiri'
|
21
|
+
gem 'capybara', '= 0.4.1.2'
|
22
|
+
gem 'factory_girl', '= 1.3.3'
|
23
|
+
gem 'factory_girl_rails', '= 1.0.1'
|
24
|
+
gem 'faker'
|
25
|
+
gem 'launchy'
|
26
|
+
|
27
|
+
if RUBY_VERSION < "1.9"
|
28
|
+
gem "ruby-debug"
|
29
|
+
else
|
30
|
+
gem "ruby-debug19"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
gem 'spree_core', :path => '/home/santhosh/Downloads/Skype Downloads/rfcommerce/dash/../core'
|
34
|
+
gem 'spree_dash', :path => '/home/santhosh/Downloads/Skype Downloads/rfcommerce/dash'
|
35
|
+
gem 'sqlite3-ruby'
|
data/LICENSE
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Copyright (c) 2007-2010, Rails Dog LLC and other contributors
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice,
|
8
|
+
this list of conditions and the following disclaimer.
|
9
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
* Neither the name Spree nor the names of its contributors may be used to
|
13
|
+
endorse or promote products derived from this software without specific
|
14
|
+
prior written permission.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
17
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
18
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
19
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
20
|
+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
21
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
22
|
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
23
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
24
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
25
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
26
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
= Overview Dashboard
|
2
|
+
|
3
|
+
Core extension to display basic graphs and charts on store activity. Can be deleted if you don't require the dashboard.
|
4
|
+
|
5
|
+
Running Tests
|
6
|
+
-------------
|
7
|
+
|
8
|
+
You need to do a quick one-time creation of a test application and then you can use it to run the tests.
|
9
|
+
|
10
|
+
rake test_app
|
11
|
+
|
12
|
+
Then run the tests
|
13
|
+
|
14
|
+
rake spec
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# this clas was inspired (heavily) from the mephisto admin architecture
|
2
|
+
|
3
|
+
class Admin::OverviewController < Admin::BaseController
|
4
|
+
before_filter :check_json_authenticity, :only => :get_report_data
|
5
|
+
#todo, add rss feed of information that is happening
|
6
|
+
|
7
|
+
def index
|
8
|
+
@show_dashboard = show_dashboard
|
9
|
+
return unless @show_dashboard
|
10
|
+
|
11
|
+
p = {:from => (Time.new().to_date - 1.week).to_s(:db), :value => "Count"}
|
12
|
+
@orders_by_day = orders_by_day(p)
|
13
|
+
@orders_line_total = orders_line_total(p)
|
14
|
+
@orders_total = orders_total(p)
|
15
|
+
@orders_adjustment_total = orders_adjustment_total(p)
|
16
|
+
@orders_credit_total = orders_credit_total(p)
|
17
|
+
|
18
|
+
@best_selling_variants = best_selling_variants
|
19
|
+
@top_grossing_variants = top_grossing_variants
|
20
|
+
@last_five_orders = last_five_orders
|
21
|
+
@biggest_spenders = biggest_spenders
|
22
|
+
@out_of_stock_products = out_of_stock_products
|
23
|
+
@best_selling_taxons = best_selling_taxons
|
24
|
+
|
25
|
+
@pie_colors = [ "#0093DA", "#FF3500", "#92DB00", "#1AB3FF", "#FFB800"]
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_report_data
|
29
|
+
opts = case params[:name]
|
30
|
+
when "7_days" then {:from => (Time.new().to_date - 1.week).to_s(:db)}
|
31
|
+
when "14_days" then {:from => (Time.new().to_date - 2.week).to_s(:db)}
|
32
|
+
when "this_month" then {:from => Date.new(Time.now.year, Time.now.month, 1).to_s(:db), :to => Date.new(Time.now.year, Time.now.month, -1).to_s(:db)}
|
33
|
+
when "last_month" then {:from => (Date.new(Time.now.year, Time.now.month, 1) - 1.month).to_s(:db), :to => (Date.new(Time.now.year, Time.now.month, -1) - 1.month).to_s(:db)}
|
34
|
+
when "this_year" then {:from => Date.new(Time.now.year, 1, 1).to_s(:db)}
|
35
|
+
when "last_year" then {:from => Date.new(Time.now.year - 1, 1, 1).to_s(:db), :to => Date.new(Time.now.year - 1, 12, -1).to_s(:db)}
|
36
|
+
end
|
37
|
+
|
38
|
+
case params[:report]
|
39
|
+
when "orders_by_day"
|
40
|
+
opts[:value] = params[:value]
|
41
|
+
|
42
|
+
render :js => "[[" + orders_by_day(opts).map { |day| "['#{day[0]}',#{day[1]}]" }.join(",") + "]]"
|
43
|
+
when "orders_totals"
|
44
|
+
render :js => [:orders_total => orders_total(opts).to_i, :orders_line_total => orders_line_total(opts).to_i,
|
45
|
+
:orders_adjustment_total => orders_adjustment_total(opts).to_i, :orders_credit_total => orders_credit_total(opts).to_i ].to_json
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
def show_dashboard
|
51
|
+
Order.count > 50
|
52
|
+
end
|
53
|
+
|
54
|
+
def conditions(params)
|
55
|
+
if params.key? :to
|
56
|
+
["completed_at >= ? AND completed_at <= ? AND state <> 'canceled'", params[:from], params[:to]]
|
57
|
+
else
|
58
|
+
["completed_at >= ? AND state <> 'canceled'", params[:from]]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def fill_empty_entries(orders, params)
|
63
|
+
from_date = params[:from].to_date
|
64
|
+
to_date = (params[:to] || Time.now).to_date
|
65
|
+
(from_date..to_date).each do |date|
|
66
|
+
orders[date] ||= []
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def orders_by_day(params)
|
71
|
+
|
72
|
+
if params[:value] == "Count"
|
73
|
+
orders = Order.select(:created_at).where(conditions(params))
|
74
|
+
orders = orders.group_by { |o| o.created_at.to_date }
|
75
|
+
fill_empty_entries(orders, params)
|
76
|
+
orders.keys.sort.map {|key| [key.strftime('%Y-%m-%d'), orders[key].size ]}
|
77
|
+
else
|
78
|
+
orders = Order.select([:created_at, :total]).where(conditions(params))
|
79
|
+
orders = orders.group_by { |o| o.created_at.to_date }
|
80
|
+
fill_empty_entries(orders, params)
|
81
|
+
orders.keys.sort.map {|key| [key.strftime('%Y-%m-%d'), orders[key].inject(0){|s,o| s += o.total} ]}
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def orders_line_total(params)
|
86
|
+
Order.sum(:item_total, :conditions => conditions(params))
|
87
|
+
end
|
88
|
+
|
89
|
+
def orders_total(params)
|
90
|
+
Order.sum(:total, :conditions => conditions(params))
|
91
|
+
end
|
92
|
+
|
93
|
+
def orders_adjustment_total(params)
|
94
|
+
Order.sum(:adjustment_total, :conditions => conditions(params))
|
95
|
+
end
|
96
|
+
|
97
|
+
def orders_credit_total(params)
|
98
|
+
Order.sum(:credit_total, :conditions => conditions(params))
|
99
|
+
end
|
100
|
+
|
101
|
+
def best_selling_variants
|
102
|
+
li = LineItem.includes(:order).where("orders.state = 'complete'").sum(:quantity, :group => :variant_id, :order => 'sum(quantity) desc', :limit => 5)
|
103
|
+
variants = li.map do |v|
|
104
|
+
variant = Variant.find(v[0])
|
105
|
+
[variant.name, v[1] ]
|
106
|
+
end
|
107
|
+
variants.sort { |x,y| y[1] <=> x[1] }
|
108
|
+
end
|
109
|
+
|
110
|
+
def top_grossing_variants
|
111
|
+
prices = LineItem.includes(:order).where("orders.state = 'complete'").sum("price * quantity", :group => :variant_id, :order => 'sum(price * quantity) desc', :limit => 5)
|
112
|
+
variants = prices.map do |v|
|
113
|
+
variant = Variant.find(v[0])
|
114
|
+
[variant.name, v[1]]
|
115
|
+
end
|
116
|
+
|
117
|
+
variants.sort { |x,y| y[1] <=> x[1] }
|
118
|
+
end
|
119
|
+
|
120
|
+
def best_selling_taxons
|
121
|
+
taxonomy = Taxonomy.last
|
122
|
+
taxons = Taxon.connection.select_rows("select t.name, count(li.quantity) from line_items li inner join variants v on
|
123
|
+
li.variant_id = v.id inner join products p on v.product_id = p.id inner join products_taxons pt on p.id = pt.product_id
|
124
|
+
inner join taxons t on pt.taxon_id = t.id where t.taxonomy_id = #{taxonomy.id} group by t.name order by count(li.quantity) desc limit 5;")
|
125
|
+
end
|
126
|
+
|
127
|
+
def last_five_orders
|
128
|
+
orders = Order.includes(:line_items).where("completed_at IS NOT NULL AND state <> 'canceled'").order("completed_at DESC").limit(5)
|
129
|
+
orders.map do |o|
|
130
|
+
qty = o.line_items.inject(0) {|sum,li| sum + li.quantity}
|
131
|
+
|
132
|
+
[o.email, qty, o.total]
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def biggest_spenders
|
137
|
+
spenders = Order.sum(:total, :group => :user_id, :limit => 5, :order => "sum(total) desc", :conditions => "completed_at is not null and state <> 'canceled' and user_id is not null")
|
138
|
+
spenders = spenders.map do |o|
|
139
|
+
orders = User.find(o[0]).orders
|
140
|
+
qty = orders.size
|
141
|
+
|
142
|
+
[orders.first.email, qty, o[1]]
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
spenders.sort { |x,y| y[2] <=> x[2] }
|
147
|
+
end
|
148
|
+
|
149
|
+
def out_of_stock_products
|
150
|
+
Product.where(:count_on_hand => 0).limit(5)
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
<h1><%= t("overview") %></h1>
|
2
|
+
|
3
|
+
<%= hook :admin_dashboard do %>
|
4
|
+
<% if @show_dashboard %>
|
5
|
+
<div class="dashboard">
|
6
|
+
<div class="dashboard_left">
|
7
|
+
<%= hook :admin_dashboard_left do %>
|
8
|
+
<div class="dashboard_small_wrapper">
|
9
|
+
<h2><%= t('best_selling_products') %></h2>
|
10
|
+
<div id="best_selling_products" style="width:50%;height:170px;float:left"></div>
|
11
|
+
<div id="pie_legend">
|
12
|
+
<% @best_selling_variants.each_with_index do |v,i| %>
|
13
|
+
<span style="background-color:<%= @pie_colors[i] %>"> </span>
|
14
|
+
<label><%= truncate v[0], :length => 27 %></label>
|
15
|
+
<div><%= number_with_delimiter v[1] %> <%= t(:units) %></div>
|
16
|
+
<% end %>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<h2><%= t('top_grossing_products') %></h2>
|
20
|
+
<div id="top_grossing_products" style="width:50%;height:170px;float:left"></div>
|
21
|
+
<div id="pie_legend">
|
22
|
+
<% @top_grossing_variants.each_with_index do |v,i| %>
|
23
|
+
<span style="background-color:<%= @pie_colors[i] %>"> </span>
|
24
|
+
<label><%= truncate v[0], :length => 27 %></label>
|
25
|
+
<div><%= number_to_currency v[1], :precision => 0 %></div>
|
26
|
+
<% end %>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
<h2><%= t('best_selling_taxons') %></h2>
|
30
|
+
<div id="best_selling_taxons" style="width:50%;height:170px;float:left"></div>
|
31
|
+
<div id="pie_legend">
|
32
|
+
<% @best_selling_taxons.each_with_index do |t,i| %>
|
33
|
+
<span style="background-color:<%= @pie_colors[i] %>"> </span>
|
34
|
+
<label><%= truncate t[0], :length => 27 %></label>
|
35
|
+
<div><%= number_with_delimiter t[1] %> <%= t(:units) %></div>
|
36
|
+
<% end %>
|
37
|
+
</div>
|
38
|
+
|
39
|
+
</div>
|
40
|
+
</div>
|
41
|
+
<% end %>
|
42
|
+
<%= hook :admin_dashboard_center do %>
|
43
|
+
<div class="dashboard_main">
|
44
|
+
<div class="dashboard_main_wrapper">
|
45
|
+
<h2 id="order_by_day_title"><%= t('orders') %> <%= t('count') %> <%= t('by_day') %> (<%= t('last_7_days') %>)</h2>
|
46
|
+
|
47
|
+
<table id="order_totals">
|
48
|
+
<tr>
|
49
|
+
<td style="width:23%;">
|
50
|
+
<p><%= t("number.currency.format.unit") %></p>
|
51
|
+
<label id="orders_total"><%= number_with_delimiter @orders_total.to_i %></label>
|
52
|
+
<span><%= t('order') %> <%= t('total') %></span>
|
53
|
+
</td>
|
54
|
+
<td class="spacer">|</td>
|
55
|
+
<td style="width:23%;">
|
56
|
+
<p><%= t("number.currency.format.unit") %></p>
|
57
|
+
<label id="orders_line_total"><%= number_with_delimiter @orders_line_total.to_i %></label>
|
58
|
+
<span><%= t('item') %> <%= t('total') %></span>
|
59
|
+
</td>
|
60
|
+
<td class="spacer">|</td>
|
61
|
+
<td style="width:23%;">
|
62
|
+
<p><%= t("number.currency.format.unit") %></p>
|
63
|
+
<label id="orders_adjustment_total"><%= number_with_delimiter @orders_adjustment_total.to_i %></label>
|
64
|
+
<span><%= t('adjustments') %></span>
|
65
|
+
</td>
|
66
|
+
<td class="spacer">|</td>
|
67
|
+
<td style="width:22%">
|
68
|
+
<p><%= t("number.currency.format.unit") %></p>
|
69
|
+
<label id="orders_adjustment_total"><%= number_with_delimiter @orders_credit_total.to_i %></label>
|
70
|
+
<span><%= t('credits') %></span>
|
71
|
+
</td>
|
72
|
+
</tr>
|
73
|
+
</table>
|
74
|
+
|
75
|
+
<div id="orders_by_day" style="width:100%;height:240px;"></div>
|
76
|
+
|
77
|
+
<div id="orders_by_day_options">
|
78
|
+
<label><%= t('range') %>:</label>
|
79
|
+
<%= select_tag "orders_by_day_reports", options_for_select([[t('last_7_days'), "7_days"], [t('last_14_days'), "14_days"], [t('this_month'), "this_month"],
|
80
|
+
[t('last_month'), "last_month"], [t('this_year'), "this_year"], [t('last_year'), "last_year"] ], "7_days") %>
|
81
|
+
<label><%= t('value') %>:</label>
|
82
|
+
<%= select_tag "orders_by_day_value", options_for_select({t('count') => 'Count', t('value') => 'Value'}, 'Count') %>
|
83
|
+
</div>
|
84
|
+
</div>
|
85
|
+
</div>
|
86
|
+
<% end %>
|
87
|
+
<%= hook :admin_dashboard_right do %>
|
88
|
+
<div class="dashboard_right">
|
89
|
+
<h2><%= t('last_5_orders') %></h2>
|
90
|
+
<table>
|
91
|
+
<thead>
|
92
|
+
<tr>
|
93
|
+
<th><%= t('name') %></th>
|
94
|
+
<th class="text-right"><%= t('items') %></th>
|
95
|
+
<th class="text-right"><%= t('total') %></th>
|
96
|
+
</tr>
|
97
|
+
</thead>
|
98
|
+
<% @last_five_orders.each do |order| %>
|
99
|
+
<tr>
|
100
|
+
<td><%= truncate order[0], :length => 18 %></td>
|
101
|
+
<td class="text-right"><%= order[1] %></td>
|
102
|
+
<td class="text-right"><%= number_to_currency order[2] %></td>
|
103
|
+
</tr>
|
104
|
+
<% end %>
|
105
|
+
</table>
|
106
|
+
|
107
|
+
<h2><%= t('5_biggest_spenders') %></h2>
|
108
|
+
<table>
|
109
|
+
<thead>
|
110
|
+
<tr>
|
111
|
+
<th><%= t('name') %></th>
|
112
|
+
<th class="text-right"><%= t('ord_qty') %></th>
|
113
|
+
<th class="text-right"><%= t('ord_total') %></th>
|
114
|
+
</tr>
|
115
|
+
</thead>
|
116
|
+
<% @biggest_spenders.each do |order| %>
|
117
|
+
<tr>
|
118
|
+
<td><%= truncate order[0], :length => 18 %></td>
|
119
|
+
<td class="text-right"><%= order[1] %></td>
|
120
|
+
<td class="text-right"><%= number_to_currency order[2] %></td>
|
121
|
+
</tr>
|
122
|
+
<% end %>
|
123
|
+
</table>
|
124
|
+
|
125
|
+
<h2><%= t('out_of_stock_products') %></h2>
|
126
|
+
<table>
|
127
|
+
<thead>
|
128
|
+
<tr>
|
129
|
+
<th><%= t('name') %></th>
|
130
|
+
</tr>
|
131
|
+
</thead>
|
132
|
+
<% @out_of_stock_products.each do |product| %>
|
133
|
+
<tr>
|
134
|
+
<td><%= product.name %></td>
|
135
|
+
</tr>
|
136
|
+
<% end %>
|
137
|
+
</table>
|
138
|
+
</div>
|
139
|
+
</div>
|
140
|
+
<% end %>
|
141
|
+
<p style="clear:both;"> </p>
|
142
|
+
<% else %>
|
143
|
+
<%= hook :admin_dashboard_welcome do %>
|
144
|
+
<%== t('overview_welcome') %>
|
145
|
+
<% end %>
|
146
|
+
<% end %>
|
147
|
+
<% end %>
|
148
|
+
|
149
|
+
<% content_for :head do %>
|
150
|
+
<%= hook :admin_dashboard_javascript do %>
|
151
|
+
<% if @show_dashboard %>
|
152
|
+
<script type="text/javascript">
|
153
|
+
var orders_by_day_points = [[<%== @orders_by_day.map { |day| "[\"#{day[0]}\",#{day[1]}]" }.join(",") %>]];
|
154
|
+
var best_selling_variants_points = [<%== @best_selling_variants.map { |v| "[\"#{h(v[0])}\",#{v[1]}]" }.join(",") %>];
|
155
|
+
var top_grossing_variants_points = [<%== @top_grossing_variants.map { |v| "[\"#{h(v[0])}\",#{v[1]}]" }.join(",") %>];
|
156
|
+
var best_selling_taxons_points = [<%== @best_selling_taxons.map { |t| "[\"#{h(t[0])}\",#{t[1]}]" }.join(",") %>];
|
157
|
+
|
158
|
+
var orders = "<%= t(:orders) %>";
|
159
|
+
var by_day = "<%= t(:by_day) %>";
|
160
|
+
|
161
|
+
var pie_colors = [<%== @pie_colors.map{|c| "'#{c}'"}.join(",") %>];
|
162
|
+
</script>
|
163
|
+
|
164
|
+
<%= javascript_include_tag 'jqPlot/jquery.jqplot.min.js', 'jqPlot/plugins/jqplot.dateAxisRenderer.min.js', 'jqPlot/plugins/jqplot.highlighter.min.js',
|
165
|
+
'jqPlot/plugins/jqplot.canvasAxisTickRenderer.min.js', 'jqPlot/plugins/jqplot.canvasTextRenderer.min.js', 'jqPlot/plugins/jqplot.canvasAxisLabelRenderer.min.js',
|
166
|
+
'jqPlot/plugins/jqplot.pieRenderer.min.js', 'dashboard.js' %>
|
167
|
+
<!--[if IE]><%= javascript_include_tag 'jqPlot/excanvas.min.js' %><![endif]-->
|
168
|
+
<% end %>
|
169
|
+
<% end %>
|
170
|
+
|
171
|
+
<%= hook :admin_dashboard_stylesheet do %>
|
172
|
+
<%= stylesheet_link_tag 'admin/dashboard.css' %>
|
173
|
+
<% end %>
|
174
|
+
<% end %>
|
data/config/routes.rb
ADDED
data/lib/spree_dash.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
namespace :spree_dash do
|
2
|
+
desc "Copies all migrations and assets (NOTE: This will be obsolete with Rails 3.1)"
|
3
|
+
task :install do
|
4
|
+
Rake::Task['spree_dash:install:migrations'].invoke
|
5
|
+
Rake::Task['spree_dash:install:assets'].invoke
|
6
|
+
end
|
7
|
+
|
8
|
+
namespace :install do
|
9
|
+
|
10
|
+
desc "Copies all migrations (NOTE: This will be obsolete with Rails 3.1)"
|
11
|
+
task :migrations do
|
12
|
+
# no migrations to migrate
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Copies all assets (NOTE: This will be obsolete with Rails 3.1)"
|
16
|
+
task :assets do
|
17
|
+
source = File.join(File.dirname(__FILE__), '..', '..', 'public')
|
18
|
+
destination = File.join(Rails.root, 'public')
|
19
|
+
puts "INFO: Mirroring assets from #{source} to #{destination}"
|
20
|
+
Spree::FileUtilz.mirror_files(source, destination)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
jQuery(document).ready(function(){
|
2
|
+
function number_with_delimiter(number, delimiter, separator) {
|
3
|
+
try {
|
4
|
+
var delimiter = delimiter || ",";
|
5
|
+
var separator = separator || ".";
|
6
|
+
|
7
|
+
var parts = number.toString().split('.');
|
8
|
+
parts[0] = parts[0].replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1" + delimiter);
|
9
|
+
formatted_number = parts.join(separator);
|
10
|
+
|
11
|
+
if(formatted_number.length>=6 && formatted_number.length<=9){
|
12
|
+
var arr = formatted_number.split(",");
|
13
|
+
return arr[0] + " k";
|
14
|
+
}else if(formatted_number.length==10){
|
15
|
+
var arr = formatted_number.split(",");
|
16
|
+
return arr[0] + " m";
|
17
|
+
}else{
|
18
|
+
return formatted_number
|
19
|
+
}
|
20
|
+
} catch(e) {
|
21
|
+
return number
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
function handle_orders_by_day(r){
|
26
|
+
var new_points = eval(r);
|
27
|
+
|
28
|
+
if(new_points[0].length>0){
|
29
|
+
orders_by_day_settings.axes.xaxis.min = new_points[0][0][0].replace(/-/g, "/");
|
30
|
+
orders_by_day_settings.axes.xaxis.max = new_points[0][new_points[0].length -1][0].replace(/-/g, "/");
|
31
|
+
}
|
32
|
+
|
33
|
+
orders_by_day_settings.axes.yaxis.label = jQuery("#orders_by_day_value :selected").val();
|
34
|
+
|
35
|
+
jQuery("#order_by_day_title").text(orders + " " + jQuery("#orders_by_day_value :selected").val() + " " + by_day + " (" + jQuery("#orders_by_day_reports :selected").text() + ")");
|
36
|
+
|
37
|
+
$('#orders_by_day').empty();
|
38
|
+
$.jqplot('orders_by_day', new_points, orders_by_day_settings);
|
39
|
+
|
40
|
+
}
|
41
|
+
|
42
|
+
function handle_orders_total(r){
|
43
|
+
var values = eval(r);
|
44
|
+
|
45
|
+
jQuery('#orders_total').text(number_with_delimiter(values[0].orders_total));
|
46
|
+
jQuery('#orders_line_total').text(number_with_delimiter(values[0].orders_line_total));
|
47
|
+
jQuery('#orders_adjustment_total').text(number_with_delimiter(values[0].orders_adjustment_total));
|
48
|
+
jQuery('#orders_adjustment_total').text(number_with_delimiter(values[0].orders_adjustment_total));
|
49
|
+
}
|
50
|
+
|
51
|
+
var orders_by_day_settings = {
|
52
|
+
title: {
|
53
|
+
textColor: '#476D9B',
|
54
|
+
fontSize: '12pt',
|
55
|
+
},
|
56
|
+
grid: {background:'#fff', gridLineColor:'#fff',borderColor: '#476D9B'},
|
57
|
+
axes:{
|
58
|
+
yaxis:{
|
59
|
+
label:'Order (Count)',
|
60
|
+
labelRenderer: $.jqplot.CanvasAxisLabelRenderer,
|
61
|
+
autoscale:true,
|
62
|
+
tickOptions:{
|
63
|
+
formatString:'%d',
|
64
|
+
fontSize: '10pt',
|
65
|
+
textColor: '#476D9B'
|
66
|
+
},
|
67
|
+
min: 0
|
68
|
+
},
|
69
|
+
xaxis:{
|
70
|
+
renderer:$.jqplot.DateAxisRenderer,
|
71
|
+
rendererOptions:{tickRenderer:$.jqplot.CanvasAxisTickRenderer},
|
72
|
+
tickOptions:{
|
73
|
+
formatString:'%b %#d, %y',
|
74
|
+
angle: -30,
|
75
|
+
fontSize: '10pt',
|
76
|
+
textColor: '#476D9B'
|
77
|
+
},
|
78
|
+
min: orders_by_day_points[0][0][0].replace(/-/g, "/"),
|
79
|
+
max: orders_by_day_points[0][orders_by_day_points[0].length -1][0].replace(/-/g, "/")//,
|
80
|
+
//tickInterval: '1 day'
|
81
|
+
}
|
82
|
+
},
|
83
|
+
series:[{lineWidth:3, color: '#0095DA', fillAndStroke: true, fill: true, fillColor: '#E6F7FF'}],
|
84
|
+
highlighter: {
|
85
|
+
formatString: "Date: %s <br/>Value: %s ",
|
86
|
+
sizeAdjust: 7.5
|
87
|
+
}
|
88
|
+
};
|
89
|
+
|
90
|
+
jQuery.jqplot('orders_by_day', orders_by_day_points, orders_by_day_settings);
|
91
|
+
|
92
|
+
jQuery("div#orders_by_day_options select").change(function(){
|
93
|
+
var report = jQuery("#orders_by_day_reports :selected").val();
|
94
|
+
var value = jQuery("#orders_by_day_value :selected").val();
|
95
|
+
|
96
|
+
jQuery.ajax({
|
97
|
+
type: 'GET',
|
98
|
+
url: 'admin/overview/get_report_data',
|
99
|
+
data: ({report: 'orders_by_day', name: report, value: value, authenticity_token: AUTH_TOKEN}),
|
100
|
+
success: handle_orders_by_day
|
101
|
+
});
|
102
|
+
|
103
|
+
jQuery.ajax({
|
104
|
+
type: 'GET',
|
105
|
+
url: 'admin/overview/get_report_data',
|
106
|
+
data: ({report: 'orders_totals', name: report, authenticity_token: AUTH_TOKEN}),
|
107
|
+
success: handle_orders_total
|
108
|
+
});
|
109
|
+
|
110
|
+
});
|
111
|
+
|
112
|
+
best_selling_variants = $.jqplot('best_selling_products', [best_selling_variants_points], {
|
113
|
+
grid: {background:'#fff',borderWidth: 0, borderColor: '#fff', shadow: false},
|
114
|
+
seriesDefaults:{
|
115
|
+
renderer:$.jqplot.PieRenderer,
|
116
|
+
rendererOptions:{padding:6,sliceMargin:0}
|
117
|
+
},
|
118
|
+
seriesColors: pie_colors
|
119
|
+
});
|
120
|
+
|
121
|
+
|
122
|
+
top_grossing_variants = $.jqplot('top_grossing_products', [top_grossing_variants_points], {
|
123
|
+
grid: {background:'#fff',borderWidth: 0, borderColor: '#fff', shadow: false},
|
124
|
+
seriesDefaults:{
|
125
|
+
renderer:$.jqplot.PieRenderer,
|
126
|
+
rendererOptions:{padding:6,sliceMargin:0}
|
127
|
+
},
|
128
|
+
|
129
|
+
seriesColors: pie_colors
|
130
|
+
});
|
131
|
+
|
132
|
+
tbest_selling_taxons = $.jqplot('best_selling_taxons', [best_selling_taxons_points], {
|
133
|
+
grid: {background:'#fff',borderWidth: 0, borderColor: '#fff', shadow: false},
|
134
|
+
seriesDefaults:{
|
135
|
+
renderer:$.jqplot.PieRenderer,
|
136
|
+
rendererOptions:{padding:6,sliceMargin:0}
|
137
|
+
},
|
138
|
+
|
139
|
+
seriesColors: pie_colors
|
140
|
+
});
|
141
|
+
|
142
|
+
|
143
|
+
});
|