Product Dashboard using Studio for ASP.NET Wijmo & MVC
The Studio for ASP.NET Wijmo suite has a web-form product dashboard sample, this sample typically shows the details about order, revenue and the sales status of different products. It utilizes Charts and Gauges to visualize data for : 1.) Monthly sales per product, 2.) Order Status, 3.) Number of new customer's added, 4.) Product wise Sales ratio, 5. ) Total Revenue & Category-wise Revenue,6.) Units Sold. You can view the sample here. The web-form sample had models for "Order", "SalesStatus", & "Report". It has a "Model data" class which generates the data at run time, the "SalesReport" class had all the linq queries that created the data-subset for visualization. The data visualization controls were populated and styled in code behind for simplicity.
To convert the same sample to an MVC application we have utilized ASP.NET MVC, jQuery, and Studio for ASP.NET Wijmo tools. In the MVC application, the model's have gone through no change. However the "SalesReport" class methods have become controller methods, which returns json data. The code behind logic now resides in a product.js file which is where we populate the widgets via AJAX and also style the charts and gauges in JavaScript. The mark-up for the product page has found its way to the "Index.cshtml" under Product in Views.
The Model
Here is the "Order" class, it has properties for, date, product, category, customer, type of shipping, amount and units ordered.
///
/// Summary description for Order
///
public class Order
{
#region private
Boolean _newCustomer;
Double _amount;
Boolean _isValid;
String _product;
String _category;
DateTime _date;
#endregion
#region Public Methods
public Order()
{
}
public Order(DateTime date, String product, String category, Boolean newCustomer, Boolean expressShipping, Double amount,int units)
{
Date = date;
Product = product;
Company = category;
ExpressShipping = expressShipping;
NewCustomer = newCustomer;
Amount = amount;
Units = units;
}
#endregion
#region Public Properties
///
/// If the customer is new.
///
public Boolean NewCustomer
{
get
{
return _newCustomer;
}
set
{
_newCustomer = value;
}
}
///
/// Amount in $
///
public Double Amount
{
get
{
return _amount;
}
set
{
_amount = value;
}
}
///
/// Is it a valid order.
///
public Boolean ExpressShipping
{
get
{
return _isValid;
}
set
{
_isValid = value;
}
}
///
/// Product Id
///
public String Product
{
get
{
return _product;
}
set
{
_product = value;
}
}
///
/// product Source
///
public String Company
{
get
{
return _category;
}
set
{
_category = value;
}
}
///
/// Date
///
public DateTime Date
{
get
{
return _date;
}
set
{
_date = value;
}
}
///
/// Number of units sold.
///
public int Units { get; set; }
#endregion
}
Here is the "SalesStatus" class which has properties for different status orders.
///
/// Summary description for Sales
///
public class SalesStatus
{
#region Public Methods
public SalesStatus()
{
}
public SalesStatus(DateTime date, String product, Int32 totalOrders, Int32 invalidOrders,
Int32 completed, Int32 returns, Int32 inOrder)
{
Date = date;
Product = product;
TotalOrders = totalOrders;
InvalidOrders = invalidOrders;
Completed = completed;
Returns = returns;
InOrder = inOrder;
}
#endregion
#region Public Properties
///
/// Bumber of users bought
///
public Int32 InOrder
{
get
{
return _inOrder;
}
set
{
_inOrder = value;
}
}
///
/// Number of qualified leads
///
public Int32 Returns
{
get
{
return _returns;
}
set
{
_returns = value;
}
}
///
/// Number of leads reched
///
public Int32 Completed
{
get
{
return _completed;
}
set
{
_completed = value;
}
}
///
/// Number of valid leads
///
public Int32 InvalidOrders
{
get
{
return _invalidOrders;
}
set
{
_invalidOrders = value;
}
}
///
/// Number of downloads
///
public Int32 TotalOrders
{
get
{
return _totalOrders;
}
set
{
_totalOrders = value;
}
}
///
/// Product ID
///
public String Product
{
get
{
return _product;
}
set
{
_product = value;
}
}
///
/// Date
///
public DateTime Date
{
get
{
return _date;
}
set
{
_date = value;
}
}
#endregion
#region Data
Int32 _inOrder;
Int32 _returns;
Int32 _completed;
Int32 _invalidOrders;
Int32 _totalOrders;
String _product;
DateTime _date;
#endregion
}
Here is the "ModelData" class which generates "orders" and "SalesStatus" data
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
///
/// Summary description for ModelData
///
public class ModelData
{
#region Data
public String GAMECONSOLE_A = "Play Station";
public String GAMECONSOLE_B = "Xbox";
public String GAMECONSOLE_C = "Nvidea Shield";
#endregion
#region Public Methods
///
/// Get Random data for orders
///
/// ListOrders
public List GetOrders()
{
List orders = new List();
orders.AddRange(CreateOders(GAMECONSOLE_A, "Sony", new DateTime(2014, 1, 1), new DateTime(2014, 12, 30), 23));
orders.AddRange(CreateOders(GAMECONSOLE_A, "Sony", new DateTime(2014, 1, 1), new DateTime(2014, 12, 30), 23));
orders.AddRange(CreateOders(GAMECONSOLE_A, "Sony", new DateTime(2014, 1, 1), new DateTime(2014, 12, 30), 23));
orders.AddRange(CreateOders(GAMECONSOLE_A, "Sony", new DateTime(2014, 1, 1), new DateTime(2014, 12, 30), 23));
orders.AddRange(CreateOders(GAMECONSOLE_A, "Sony", new DateTime(2014, 1, 1), new DateTime(2014, 12, 30), 23));
orders.AddRange(CreateOders(GAMECONSOLE_A, "Sony", new DateTime(2014, 1, 1), new DateTime(2014, 12, 30), 23));
orders.AddRange(CreateOders(GAMECONSOLE_B, "Microsoft", new DateTime(2014, 1, 1), new DateTime(2014, 12, 30), 23));
orders.AddRange(CreateOders(GAMECONSOLE_B, "Microsoft", new DateTime(2014, 1, 1), new DateTime(2014, 12, 30), 23));
orders.AddRange(CreateOders(GAMECONSOLE_B, "Microsoft", new DateTime(2014, 1, 1), new DateTime(2014, 12, 30), 23));
orders.AddRange(CreateOders(GAMECONSOLE_B, "Microsoft", new DateTime(2014, 1, 1), new DateTime(2014, 12, 30), 23));
orders.AddRange(CreateOders(GAMECONSOLE_B, "Microsoft", new DateTime(2014, 1, 1), new DateTime(2014, 12, 30), 23));
orders.AddRange(CreateOders(GAMECONSOLE_B, "Microsoft", new DateTime(2014, 1, 1), new DateTime(2014, 12, 30), 23));
orders.AddRange(CreateOders(GAMECONSOLE_C, "NVIDIA", new DateTime(2014, 1, 1), new DateTime(2014, 12, 30), 23));
orders.AddRange(CreateOders(GAMECONSOLE_C, "NVIDIA", new DateTime(2014, 1, 1), new DateTime(2014, 5, 30), 23));
orders.AddRange(CreateOders(GAMECONSOLE_C, "NVIDIA", new DateTime(2014, 5, 1), new DateTime(2014, 12, 30), 23));
orders.AddRange(CreateOders(GAMECONSOLE_C, "NVIDIA", new DateTime(2014, 3, 1), new DateTime(2014, 12, 30), 23));
orders.AddRange(CreateOders(GAMECONSOLE_C, "NVIDIA", new DateTime(2014, 1, 1), new DateTime(2014, 12, 30), 23));
orders.AddRange(CreateOders(GAMECONSOLE_C, "NVIDIA", new DateTime(2014, 1, 1), new DateTime(2014, 12, 30), 23));
orders.AddRange(CreateOders(GAMECONSOLE_C, "NVIDIA", new DateTime(2014, 1, 1), new DateTime(2014, 12, 30), 23));
return orders;
}
///
/// Get sample data for sales status
///
///
///
public List GetSalesStatus(List orders)
{
List salesStatus = new List();
List sc1 = CreateSalesStatus(orders, GAMECONSOLE_A, new DateTime(2014, 1, 1), new DateTime(2014, 12, 30));
salesStatus.AddRange(sc1);
List sc2 = CreateSalesStatus(orders, GAMECONSOLE_B, new DateTime(2014, 1, 1), new DateTime(2014, 12, 30));
salesStatus.AddRange(sc2);
List sc3 = CreateSalesStatus(orders, GAMECONSOLE_C, new DateTime(2014, 1, 1), new DateTime(2014, 12, 30));
salesStatus.AddRange(sc3);
return salesStatus;
}
public List<KeyValuePair<string, string>> GetProduct()
{
List<KeyValuePair<string, string>> prodList= new List<KeyValuePair<string, string>>();
prodList.Add(new KeyValuePair<string, string>("GAMECONSOLE\_A", GAMECONSOLE\_A));
prodList.Add(new KeyValuePair<string, string>("GAMECONSOLE\_B",GAMECONSOLE\_B));
prodList.Add(new KeyValuePair<string, string>("GAMECONSOLE\_C",GAMECONSOLE\_C));
return prodList;
}
#endregion
#region Private Methods
private List CreateSalesStatus(List orders, String productName, DateTime fromDate, DateTime toDate)
{
List sales = new List();
Double randVal;
Int32 sold = 0;
Random rand = new Random(DateTime.Now.Millisecond);
while (fromDate < toDate)
{
sold = (from x in orders where x.Product == productName && x.Date.Month == fromDate.Month select x).Count();
randVal = rand.NextDouble();
Int32 p1 = (Int32)(sold * (1 + randVal < 1 ? 1.5 : 2));
randVal = rand.NextDouble();
Int32 p2 = (Int32)(p1 * (1 + randVal < 1 ? 1.5 : 2));
randVal = rand.NextDouble();
Int32 p3 = (Int32)(p2 * (1 + randVal < 1 ? 1.5 : 2));
Int32 p4 = p1 + p2 + p3 + sold;
sales.Add(new SalesStatus(fromDate, productName, p4, p1, p3, p2, sold));
fromDate = fromDate.AddMonths(1);
}
return sales;
}
private List CreateOders(String productName, string category, DateTime fromDate, DateTime toDate, Int32 totalTargetSold)
{
Random rand = new Random(DateTime.Now.Millisecond);
List orders = new List();
Int32 count = 1;
try
{
while (fromDate <= toDate) { Int32 randomNumber = rand.Next(10, 100 * count); Int32 productSold = (Int32)(randomNumber * 0.123 * rand.NextDouble() * rand.NextDouble()); Int32 index = productSold; int loop = 2; while (loop >= 0)
{
Int32 rand1 = rand.Next(1, 300);
Int32 rand2 = rand.Next(100, 200);
Boolean newCustomer = !(rand.Next(1, 5) == 2 || rand.Next(1, 5) == 5);
Boolean expressShip = rand.Next(index, index + 5) == 3;
Double revenue = (productName == GAMECONSOLE\_A) ? 225 : (productName == GAMECONSOLE\_B) ? 325 : 200;
int units = rand2;
orders.Add(new Order(fromDate, productName, category, newCustomer, expressShip, revenue, units));
loop--;
}
fromDate = fromDate.AddDays(1);
}
}
catch (Exception e)
{
throw new Exception(e.Message);
}
return orders;
}
#endregion
}
The sample uses other classes as part of the sales report data, here is the class listing
public class ProductRevenue
{
public double Amount { get; set; }
public string Product { get; set; }
public int Units { get; set; }
public string Company { get; set; }
}
public class Customers
{
public double OldCustomer { get; set; }
public double NewCustomer { get; set; }
public string Product { get; set; }
}
public class SalesUnits
{
public double Amount { get; set; }
public string Product { get; set; }
public string Company { get; set; }
public double Units { get; set; }
public string Day { get; set; }
}
The Controller
Now that we have the model created let's create a product controller that will have methods to return data subsets for our visualization controls.
public class ProductController : Controller
{
#region privateData
static List orders = new List();
static List sales = new List();
string _ordersdata = string.Empty;
string _salesdata = string.Empty;
string _productsdata = string.Empty;
int selectedMonth = default(int);
string selectedProduct = string.Empty;
static ModelData model;
public ProductController()
{
this.\_ordersdata = AppDomain.CurrentDomain.BaseDirectory+ "//App\_Data//ordersdata.xml";
this.\_salesdata = AppDomain.CurrentDomain.BaseDirectory+ "//App\_Data//salesdata.xml";
model = new ModelData();
if (orders.Count > 0 && sales.Count > 0)
{
}
else
{
if (System.IO.File.Exists(\_ordersdata) && System.IO.File.Exists(\_salesdata))
{
orders = this.GetOrdersData(_ordersdata);
sales = this.GetSalesData(_salesdata);
}
else
{
orders = model.GetOrders();
sales = model.GetSalesStatus(orders);
this.SaveOrdersData(_ordersdata, orders);
this.SaveSalesData(_salesdata, sales);
}
}
}
#endregion
// GET: Product
public ActionResult Index()
{
return View();
}
public ActionResult Product()
{
return View();
}
///
/// Gets product wise revenue
///
///
///
///
public JsonResult GetProductsRevenue(int SelMonth, string SelProduct)
{
this.SelectedMonth = SelMonth;
this.SelectedProduct = SelProduct;
var prod = from order in orders
where order.Date.Month == SelMonth && order.Product == SelProduct
group order by order.Date.Day into grpOrder
select new SalesUnits
{
Day = grpOrder.First().Date.Day.ToString(),
Amount = Math.Round(grpOrder.Sum(x => x.Amount * x.Units) / 100),
Units = grpOrder.Sum(x => x.Units),
Company = grpOrder.First().Company,
Product = grpOrder.First().Product
};
return Json(prod.ToList(), JsonRequestBehavior.AllowGet);
}
///
/// Gets the products
///
///
public JsonResult GetProducts()
{
return Json(model.GetProduct(), JsonRequestBehavior.AllowGet);
}
///
/// Category wise revenue
///
///
///
///
public JsonResult GetCategoryRevenue(int SelMonth, string SelProduct)
{
this.SelectedMonth = SelMonth;
var categoryrev = from order in orders
where order.Date.Month == SelectedMonth
group order by order.Company into grpOrder
select new ProductRevenue()
{
Company = grpOrder.First().Company,
Amount = grpOrder.Sum(x => x.Amount * x.Units),
Units = grpOrder.Sum(x => x.Units),
Product = grpOrder.First().Product
};
return Json(categoryrev.ToList(), JsonRequestBehavior.AllowGet);
}
public IEnumerable NewCustomerAdded
{
get
{
var cust = (from order in orders
where order.NewCustomer && order.Date.Month == SelectedMonth
orderby order.Date
group order by order.Product into grpOrders
select new KeyValuePair<string, Double>(grpOrders.First().Product, grpOrders.Count())).ToList();
return cust;
}
}
public IEnumerable ExistingCustomers
{
get
{
var cust = (from order in orders
where !order.NewCustomer && order.Date.Month == SelectedMonth
orderby order.Date
group order by order.Product into grpOrders
select new KeyValuePair<string, Double>(grpOrders.First().Product, grpOrders.Count())).ToList();
return cust;
}
}
///
/// Get ratio of old Vs new customer
///
///
///
public JsonResult GetCustomerRatio(int SelMonth)
{
List custratio = new List();
this.SelectedMonth = SelMonth;
foreach (KeyValuePair<string, Double> cust in NewCustomerAdded)
{
foreach (KeyValuePair<string, Double> oldcust in ExistingCustomers)
{
if (oldcust.Key == cust.Key)
{
Customers c = new Customers();
c.Product = cust.Key;
c.NewCustomer = cust.Value;
c.OldCustomer = oldcust.Value;
custratio.Add(c);
break;
}
}
}
return Json(custratio, JsonRequestBehavior.AllowGet);
}
private double SelectedProductSales
{
get
{
var prodsale = (from order in orders
where order.Product == SelectedProduct && order.Date.Month == SelectedMonth
select order.Amount * order.Units).Sum();
return prodsale;
}
}
///
/// Gets selected product revenue and total revenue
///
///
///
///
public JsonResult GetSelectedProductSale(int SelMonth, string SelProduct)
{
this.SelectedMonth = SelMonth;
this.SelectedProduct = SelProduct;
double[] revenue = new double[2] { SelectedProductSales ,TotalSales};
return Json(revenue, JsonRequestBehavior.AllowGet);
}
///
/// Gets ratio of different order status
///
///
///
///
public JsonResult GetOrderStatusRatio(int SelMonth, string SelProduct)
{
this.SelectedMonth = SelMonth;
this.SelectedProduct = SelProduct;
var ordratio = (from sale in sales
where sale.Product == SelectedProduct
&& sale.Date.Month == SelectedMonth
group sale by sale.Date.Month into grpSales
select new
{
CompletedOrders = grpSales.Sum(x => x.Completed),
InvalidOrders = grpSales.Sum(x => x.InvalidOrders),
InOrder = grpSales.Sum(x => x.InOrder),
TotalReturns = grpSales.Sum(x => x.Returns)
}).FirstOrDefault();
List<KeyValuePair<string, double>> orderratio = new List<KeyValuePair<string, double>>();
if (!ordratio.Equals(null))
{
orderratio.Add(new KeyValuePair<string, double>("Completed", ordratio.CompletedOrders));
orderratio.Add(new KeyValuePair<string, double>("Invalid", ordratio.InvalidOrders));
orderratio.Add(new KeyValuePair<string, double>("InOrder", ordratio.InOrder));
orderratio.Add(new KeyValuePair<string, double>("Total Returns", ordratio.TotalReturns));
}
return Json(orderratio, JsonRequestBehavior.AllowGet);
}
///
/// Gets percentage of products sale against total sales
///
///
///
public JsonResult GetSalesRatio(int SelMonth)
{
this.SelectedMonth = SelMonth;
double totalsale = TotalSales;
var saleratio = (from order in orders
where order.Date.Month == SelectedMonth
group order by order.Product into grpOrder
select new
{
Product = grpOrder.First().Product,
SalesRatio =Math.Round( (grpOrder.Sum(x => x.Amount * x.Units) / TotalSales) * 100),
Units = grpOrder.Sum(x => x.Units)
}).ToList();
return Json(saleratio, JsonRequestBehavior.AllowGet);
}
double selectedProductSales = default(double);
public double SalesPercentage
{
get
{
return (SelectedProductSales / TotalSales) * 100;
}
}
public double TotalSales
{
get
{
var totalsales = (from order in orders
where order.Date.Month == SelectedMonth
select order.Amount * order.Units).Sum();
return totalsales;
}
}
public int SelectedMonth
{
get { return selectedMonth; }
set { selectedMonth = value; }
}
public string SelectedProduct
{
get { return selectedProduct; }
set { selectedProduct = value; }
}
XmlSerializer ordersSerializer = new XmlSerializer(typeof(List));
///
/// Save Order to xml data file
///
///File to which data is to be saved
///List of order items
public void SaveOrdersData(string fileName, List orderList)
{
using (FileStream fs = System.IO.File.OpenWrite(fileName))
{
ordersSerializer.Serialize(fs, orderList);
}
}
///
/// Get order data from xml file.
///
///File from which data is to be retreived
/// List of Order's
public List GetOrdersData(string fileName)
{
using (FileStream fs = System.IO.File.OpenRead(fileName))
{
List list = (List)ordersSerializer.Deserialize(fs);
return list;
}
}
XmlSerializer salesStatusSerializer = new XmlSerializer(typeof(List));
///
/// Save Sales data to xml file.
///
///File to which data is to be saved
///List of sales status
public void SaveSalesData(string fileName, List salesList)
{
using (FileStream fs = System.IO.File.OpenWrite(fileName))
{
salesStatusSerializer.Serialize(fs, salesList);
}
}
///
/// Get sales data from xml file.
///
///File from which data is to be retrieved
/// List of sales status
public List GetSalesData(string fileName)
{
using (FileStream fs = System.IO.File.OpenRead(fileName))
{
List list = (List)salesStatusSerializer.Deserialize(fs);
return list;
}
}
}
}
The View
Now that we have the model and controller in place, lets see the view, here is the mark-up code which uses Bootstrap to give the page its required structure
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}</pre>
<div class="container-fluid">
<div class="row">
<div class=".col-xs-12 col-md-12"><header class="header">PRODUCT REVENUE</header>
<div class="wjStyle" id="productUnits"></div>
</div>
</div>
<div class="row">
<div class=".col-xs-6 col-md-6"><header class="header">NEW CUSTOMER VS OLD CUSTOMER</header>
<div class="wjStyle" id="customerRatio"></div>
</div>
<div class=" .col-xs-6 col-md-3"><header class="header">ORDER RATIO</header>
<div class="wjStyle" id="orderRatio"></div>
</div>
<div class=" .col-xs-6 col-md-3"></div>
</div>
<div class="row">
<div class=" .col-xs-6 col-md-3"><header class="header">UNIT RATIO</header>
<div class="wjStyle3" id="categoryUnits"></div>
</div>
<div class=" .col-xs-6 col-md-3"><header class="header">SALES RATIO</header>
<div class="wjStyle3" id="salesRatio"></div>
</div>
<div class="col-md-2"></div>
<div class="col-md-2"></div>
<div class="col-md-2"></div>
</div>
</div>
<pre>
Here is the JavaScript that creates and binds data to the Wijmo charts and gauges. Note that we simply set the data property of the charts to the json data returned by the controller, the styling for chart series and gauge is done during initialization.
productApp = {};
productApp.selectedMonth = 1;
productApp.selectedProduct = "Play Station";
productApp.Updating = true;
productApp.Init = function () {
getProducts();
var cbprod=$("#product").wijcombobox({
isEditable: false,
selectedIndex: 0,
selectedIndexChanged: function (e, data) {
var val = cbprod.wijcombobox("option", "selectedValue");
if (val)
{
productApp.selectedProduct = cbprod.wijcombobox("option", "selectedValue");
}
refreshReport();
}
});
var cbmonth= $("#month").wijcombobox({
isEditable: false,
selectedIndex: 0,
data: getMonths(),
selectedIndexChanged: function (e, data) {
productApp.selectedMonth = cbmonth.wijcombobox("option", "selectedIndex") + 1;
refreshReport();
}
});
$("#productUnits").wijcompositechart({
animation:{duration:2000,easing:"easeOutCubic"},
showChartLabels: false,
seriesStyles: [{
fill: "#FFCC66",
stroke: "#FF9900",
strokeWidth:3
},
{
fill: "#FF0000",
stroke:"Red"
}],
axis: {
y: [
{
text:"Revenue",
},
{
text:"Units",
compass: "east",
min: 500,
max: 4000,
gridMajor: {
visible: false
}
}
]
},
data: {
x: { bind: "Day" }
},
legend:{compass:"north",orientation:"horizontal"}
});
$("#customerRatio").wijbarchart({
animation: { duration: 2000, easing: "EaseInOutCubic" },
seriesStyles: [{
fill: "#FF9900",
stroke: "#FF9900",
strokeWidth: 3
},
{
fill: "#3399FF"
}],
data: {
x: { bind: "Product" }
},
legend: {
compass: "east",
},
autoResize: true,
clusterRadius: 12
});
$("#orderRatio").wijpiechart({
animation:{duration:1000,easing:"EaseOutBounce"},
innerRadius: 70,
radius: 90,
seriesStyles: [{
fill: "#ff9900",
},
{
fill: "#FFCC66",
}, {
fill: "#3399FF",
},
{
fill: "#ff6600"
}],
data: {
label: { bind: "Key" },
value: { bind: "Value" },
},
});
$("#salesRatio").wijbubblechart({
animation: { duration: 1000, easing: "EaseOutBounce" },
seriesStyles:[ { fill: "#ff9900",stroke:"Red" }],
autoResize: true,
hint: { content: function () { return (this.data.y + "%") } },
legend: {
compass: "north"
},
data: {
x: { bind: "Product" }
}
});
$("#categoryUnits").wijlinechart({
seriesStyles: [{ fill: "#FFCC66", stroke: "Red" }],
autoResize:true,
type: "area",
legend: {
compass: "north"
}
});
$("#revenue").wijradialgauge({
height: 230,
width: 230,
value: 10,
max: 60,
min: 0,
startAngle: 0,
sweepAngle: 180,
radius: 100,
islogarithmic: false,
origin: {
x: 0.5, y: 0.5
},
labels: {
offset: -30, //4F6B82
style: {
fill: "#556A7C",
stroke: "none"
}
},
tickMinor: {
position: "inside",
style: {
fill: "#556A7C",
stroke: "#556A7C"
},
interval: 2,
visible: true,
offset: 1
},
tickMajor: {
position: "center",
style: {
fill: "#556A7C",
stroke: "#556A7C"
},
interval: 5,
visible: true
},
face: {
style: {
fill: "#ff9900",
stroke: "#996633",
opacity:0.6
}
},
pointer: {
length: 1,
style: {
fill: "#BF551C",
stroke: "#BF551C"
}
},
cap: {
style: {
fill: "#3399FF",
stroke: "#7F9CAD",
}
}
});
$("#categorySalesMS").wijradialgauge({
height: 260,
width:170,
value: 10,
max: 30,
min: 0,
sweepAngle: 90,
radius: 90,
islogarithmic: false,
origin: {
x: 0.7, y: 0.6
},
labels: {
offset: 5, //4F6B82
style: {
fill: "#556A7C",
stroke: "none"
}
},
tickMinor: {
position: "center",
style: {
fill: "#FFCC66",
stroke: "#556A7C",
height: 0.5,
width:10
},
interval: 1,
visible: true,
offset: 5,
factor:3
},
tickMajor: {
position: "center",
style: {
fill: "#556A7C",
stroke: "#556A7C",
height: 2,
width:20
},
interval: 5,
factor:1,
offset:1,
visible: true
},
ranges: [{
startWidth: 20,
endWidth: 20,
startValue: 0,
endValue: 30,
startDistance: 0.7,
endDistance: 0.7,
style: {
fill: "#ff9900",
opacity:0.8
}
}],
face: {
style: {
fill: "transparent",
stroke: {width:0}
}
},
pointer: {
length: 0.8,
width:20,
style: {
fill: "#BF551C",
stroke: "#BF551C"
}
},
cap: {
radius:20,
style: {
fill: "#ff9900",
stroke: "#7F9CAD"
}
}
});
$("#categorySalesSony").wijradialgauge({
height: 260,
width: 170,
value: 10,
max: 30,
min: 0,
sweepAngle: 90,
radius: 90,
islogarithmic: false,
origin: {
x: 0.7, y: 0.6
},
labels: {
offset: 5, //4F6B82
style: {
fill: "#556A7C",
stroke: "none"
}
},
tickMinor: {
position: "center",
style: {
fill: "#556A7C",
stroke: "#556A7C",
height: 0.5,
width: 10
},
interval: 1,
visible: true,
offset: 5,
factor: 3
},
tickMajor: {
position: "center",
style: {
fill: "#556A7C",
stroke: "#556A7C",
height: 2,
width: 20
},
interval: 5,
factor: 1,
offset: 1,
visible: true
},
ranges: [{
startWidth: 20,
endWidth: 20,
startValue: 0,
endValue: 30,
startDistance: 0.7,
endDistance: 0.7,
style: {
fill: "#ff9900",
opacity: 0.8
}
}],
face: {
style: {
fill: "transparent",
stroke: { width: 0 }
}
},
pointer: {
length: 0.8,
width: 20,
style: {
fill: "#BF551C",
stroke: "#BF551C"
}
},
cap: {
radius: 20,
style: {
fill: "#ff9900",
stroke: "#7F9CAD"
}
}
});
$("#categorySalesNv").wijradialgauge({
height: 260,
width: 170,
value: 10,
max: 30,
min: 0,
sweepAngle: 90,
radius: 90,
islogarithmic: false,
origin: {
x: 0.7, y: 0.6
},
labels: {
offset: 5, //4F6B82
style: {
fill: "#556A7C",
stroke: "none"
}
},
tickMinor: {
position: "center",
style: {
fill: "#556A7C",
stroke: "#556A7C",
height: 0.5,
width: 10
},
interval: 1,
visible: true,
offset: 5,
factor: 3
},
tickMajor: {
position: "center",
style: {
fill: "#556A7C",
stroke: "#556A7C",
height: 2,
width: 20
},
interval: 5,
factor: 1,
offset: 1,
visible: true
},
ranges: [{
startWidth: 20,
endWidth: 20,
startValue: 0,
endValue: 30,
startDistance: 0.7,
endDistance: 0.7,
style: {
fill: "#ff9900",
opacity: 0.8
}
}],
face: {
style: {
fill: "transparent",
stroke: { width: 0 }
}
},
pointer: {
length: 0.8,
width: 20,
style: {
fill: "#BF551C",
stroke: "#BF551C"
}
},
cap: {
radius: 20,
style: {
fill: "#ff9900",
stroke: "#7F9CAD"
}
}
});
}
function getMonths()
{
return [{label:'January',value:'January'}, {label:'February',value:'February'}, {label:'March',value:'March'},{label: 'April',value:'April'},
{ label: 'May', value: 'May' }, { label: 'June', value: 'June' }, { label: 'July', value: 'July' }, { label: 'August', value: 'August' },
{ label: 'September', value: 'September' }, { label: 'October', value: 'October' }, { label: 'November', value: 'November' }, { label: 'December', value: 'December' }];
}
function getProducts()
{
productApp.Updating = true;
$.ajax({
url: "Product/GetProducts",
dataType: "json",
data: {},
success: function (data) {
if (data && data.length) {
var productview = [];
for (i = 0; i < data.length;i++) { var items = { label: data[i].Value, value: data[i].Value }; productview.push(items); } if (productview.length > 0)
{
$("#product").wijcombobox("option", "data", productview);
$("#product").wijcombobox("option", "selectedIndex", 0);
}
}
}
});
}
function refreshReport() {
loadRevenue();
loadCustomerRatio();
loadOrderRatio();
loadSalesRatio();
loadCategorySales();
selectedProductSale();
}
function loadRevenue() {
$.ajax({
url: "Product/GetProductsRevenue",
dataType: "json",
data: { SelMonth: productApp.selectedMonth, SelProduct: productApp.selectedProduct },
success: function (data) {
if (data && data.length) {
var seriesList = [];
var unitSeries = {
type: "scatter",
legendEntry: true,
label: "Units Sold",
data: {
//x: { bind: "Day" },
y: { bind: "Units" }
},
yAxis: 1
};
var revnueSeries = {
type: "bezier",
label: "Revenue",
legendEntry: true,
markers:{visible:true,type:"tri"},
data: {
//x: { bind: "Day" },
y: { bind: "Amount" }
}
};
seriesList.push(revnueSeries);
seriesList.push(unitSeries);
$("#productUnits").wijcompositechart("option", "seriesList", seriesList);
$("#productUnits").wijcompositechart("option", "dataSource", data);
}
}
});
}
function loadCustomerRatio() {
$.ajax({
url: "Product/GetCustomerRatio",
dataType: "json",
data: { SelMonth:productApp.selectedMonth },
success: function (data) {
if (data && data.length) {
var seriesList = [];
var oldSeries = {
label: "Existing Customers",
data: {
y:{bind:"OldCustomer"}
}
}
var newSeries = {
label:"New Customers",
data: {
y: { bind: "NewCustomer" }
}
}
seriesList.push(oldSeries);
seriesList.push(newSeries);
$("#customerRatio").wijbarchart("option", "seriesList", seriesList);
$("#customerRatio").wijbarchart("option", "dataSource", data);
}
}
});
}
function loadOrderRatio() {
$.ajax({
url: "Product/GetOrderStatusRatio",
dataType: "json",
data: { SelMonth: productApp.selectedMonth, SelProduct: productApp.selectedProduct },
success: function (data) {
if (data && data.length) {
$("#orderRatio").wijpiechart("option", "dataSource", data);
}
}
});
}
function loadSalesRatio() {
$.ajax({
url: "Product/GetSalesRatio",
dataType: "json",
data: { SelMonth: productApp.selectedMonth },
success: function (data) {
if (data && data.length) {
var seriesList = [];
var salesSeries = {
label: "Sales %",
data: {
y: { bind: "SalesRatio" },
y1: { bind: "SalesRatio" }
}
}
seriesList.push(salesSeries);
$("#salesRatio").wijbubblechart("option", "seriesList", seriesList);
$("#salesRatio").wijbubblechart("option","dataSource",data);
}
}
});
}
function loadCategorySales() {
$.ajax({
url: "Product/GetCategoryRevenue",
dataType: "json",
data: { SelMonth: productApp.selectedMonth },
success: function (data) {
if (data && data.length) {
var seriesList = [];
var lineSeries = {
fitType: "spline",
label: "Units Sold",
markers:{visible:true,type:"circle"},
data: {
x: { bind: "Product" },
y: { bind: "Units" }
}
}
seriesList.push(lineSeries);
$("#categoryUnits").wijlinechart("option", "seriesList", seriesList);
$("#categoryUnits").wijlinechart("option", "dataSource", data);
var mssales =(data[1].Amount);
var pssales = (data[0].Amount);
var nvsales = (data[2].Amount);
$("#categorySalesMS").wijradialgauge("option", "value", mssales / 1000000);
$("#categorySalesSony").wijradialgauge("option", "value", pssales / 1000000);
$("#categorySalesNv").wijradialgauge("option", "value", pssales / 1000000);
$("#revenueMSlbl").text("MS: " + Globalize.format(mssales,"c"));
$("#revenueSonylbl").text("SONY: " + Globalize.format(pssales,"c"));
$("#revenueNvlbl").text("NVIDEA: " + Globalize.format(nvsales, "c"));
}
}
});
}
function selectedProductSale()
{
$.ajax({
url: "Product/GetSelectedProductSale",
dataType: "json",
data: { SelMonth: productApp.selectedMonth,SelProduct:productApp.selectedProduct },
success: function (data) {
if (data) {
var sales = data[0] / 1000000;
var totalSales = data[1] / 1000000;
$("#revenue").wijradialgauge("option", "value", data[0] / 1000000);
var ranges = [{
startWidth: 15,
endWidth: 20,
startValue: sales - 10,
endValue: sales+10,
startDistance: 0.8,
endDistance: 0.8,
style: {
fill: "#CC6633",
stroke: "#BC8A8E",
opacity: 0.6
}
}
];
$("#revenue").wijradialgauge("option", "ranges", ranges);
$("#revenue").wijradialgauge("option", "max", totalSales);
$("#revenuelbl").text("REVENUE: " + Globalize.format(data[0],"c"));
}
}
});
}
The reference for the script file is added to "_layout.cshtml". That completes all the changes in this sample. Here is a preview:
You can download the complete sample here ProductDashboardMVC.