ASP.NET MVC Sales Dashboard Application
In an older blog entry, we looked at creating a sales dashboard application for ASP.NET. Today, we will briefly describe how to achieve the same setup in MVC. The complete sample is available for download at the bottom of the page.
To start off, we create a new Visual Studio 2012 MVC application, with .NET Framework 4.0. To be able to take advantage of the charting component, we add a reference to the Shield.Mvc.UI dll:
The folder structure of the application looks like this:
In the next paragraphs we will go over the contents of each folder of interest and give some additional information on the code used, along with its importance. We start with the Views folder. It contains four different views – three for the charts and one for the main page structure. The first view is “Layout.cshtml”, which determines the main layout of the page and includes references to any required css files, along with the js files, needed by the charting component and its wrapper:
<html> <head> <meta name="viewport" content="width=device-width" /> <title>Sales Dashboard - Shield Chart for ASP.NET MVC</title> <link rel="stylesheet" type="text/css" href="content/shield-chart.1.2.3-Trial/css/shield-chart.min.css" /> <script src="content/shield-chart.1.2.3-Trial/js/jquery-1.9.1.min.js" type="text/javascript"></script> <script src="content/shield-chart.1.2.3-Trial/js/shield-chart.all.min.js" type="text/javascript"></script> <link rel="stylesheet" type="text/css" href="content/css/site.css" /> <script src="content/js/scripts.js" type="text/javascript"></script> </head> <body> <div class="page"> <div class="header"> </div> <div class="main"> @RenderBody() </div> <div class="clear"> </div> </div> <div class="footer"> </div> </body> </html>
This view also contains a reference to “scripts.js” – a file which contains the handlers for the client side events of the charts, and is discussed in more detail at the end of the article.
The next view, “Index.cshtml” contains the declaration of the “quarterly” chart – the only one which is rendered initially, and the selection of which populates the related donut chart. The declaration includes setting all the required properties of the control, such as its X and Y axes, or the Data Series and their actual data. A point of interest is the “Events” property, which declares a handler for selecting a point from the bar chart. This is important in relating the chart with a sub-chart, which will render once the user makes a selection from the first chart. The code on the page looks like this:
@{ ViewBag.Title = "Sales Dashboard with Shield Chart for ASP.NET MVC"; Layout = "~/Views/Shared/Layout.cshtml"; } @model IEnumerable<div class="dashboard"> <div class="header"> Sales DashBoard using <span class="highlight">Shield UI MVC Chart</span> </div> <div class="topleft"> @(Html.ShieldChart(Model) .Name("quarterlySales") .HtmlAttribute("class", "chart") .PrimaryHeader(header => header.Text("Quarterly Sales")) .Export(false) .Tooltip(tooltip => tooltip.CustomPointText("Sales Volume: {point.y}")) .AxisX(axisX => axisX.CategoricalValues(model => model.Quarter)) .AxisY(axisY => axisY.Title(title => title.Text("Quarterly Overview"))) .DataSeries(dataSeries => dataSeries .Bar() .Data(model => model.Sales) .EnablePointSelection(true)) .ChartLegend(chartLegend => chartLegend .Align(Shield.Mvc.UI.Chart.Align.Center)) .Events(events => events.PointSelect("app.quarterSelected"))) </div> <div class="topright"> </div> <div class="bottom"> </div> </div>
The first sub-chart, which is populated with data once the user selects a quarter from the initially rendered bar chart, is a donut chart. Its declaration is contained in the “_PerformanceChart.cshtml” view. Its code is as follows:
@model IEnumerable<SalesDashboardMVC.Models.PerformanceData> @(Html.ShieldChart(Model) .Name("productsByQuarter") .HtmlAttribute("class", "chart") .Export(false) .PrimaryHeader(header => header.Text("Select a Quarter to show products sales")) .AxisY(axisY => axisY.Title(title => title.Text("Break-Down for selected quarter"))) .DataSeries(dataSeries => dataSeries .Donut() .Name("Q Data") .Data(model => new { collectionAlias = model.Product, x = model.Product, y = model.Data, }) .EnablePointSelection(true) .AddToLegend(true)) .ChartLegend(chartLegend => chartLegend.Align(Shield.Mvc.UI.Chart.Align.Center)) .Events(events => events.PointSelect("app.productSelected")))
The last chart, which is populated when an item from the donut is selected. It is located in the “_SalesDetailsChart.cshtml” file and has the following declaration:
@model IEnumerable@(Html.ShieldChart(Model) .Name("salesDetails") .HtmlAttribute("class", "chart") .PrimaryHeader(header => header.Text("Select a product to show sales details")) .Export(false) .DataSeries(dataSeries => dataSeries .Line() .Data(model => model.QuarterSales) .AddToLegend(false)))
The “Models” folder contains the models for the data, which will be used to populate the three different charts. For example, the first chart is bound to data of type “QuarterlySales” and looks like this:
namespace SalesDashboardMVC.Models { public class QuarterlySales { public string Quarter { get; set; } public decimal Sales { get; set; } public static IEnumerableGetData() { yield return new QuarterlySales() { Quarter = "Q1", Sales = 312 }; yield return new QuarterlySales() { Quarter = "Q2", Sales = 212 }; yield return new QuarterlySales() { Quarter = "Q3", Sales = 322 }; yield return new QuarterlySales() { Quarter = "Q4", Sales = 128 }; } } }
The other charts are bound to similar data classes, which I will omit for brevity. The “Controllers” folder contains a single controller, which governs the actions triggered by selections in the charts. It looks like this:
public class HomeController: Controller { // // GET: /Home/ public ActionResult Index() { return View(QuarterlySales.GetData()); } public ActionResult Performance(string quarter) { return View("_PerformanceChart", PerformanceData.GetDataByQuarter(quarter)); } public ActionResult Details(string product, string quarter) { return View("_SalesDetailsChart", SalesByProduct.GetDataByProductAndQuarter(product, quarter)); } }
One important piece of code remains to be mentioned. This is the “scripts.js” file, located in the “Content” folder. As can be seen from the declarations above, the first and second charts have selection event handlers. These handle client side events, which are triggered when the user selects a bar or donut segment from one of the charts. The “scripts.js” file contains client side handlers for these events, which look like this:
(function (jQuery) { this.app = { quarter: "", quarterSelected: function (e) { var quarter = app.quarter = e.point.name; $(".topright").load("/performance/" + quarter); $(".bottom").empty(); }, productSelected: function (e) { var product = e.point.x, quarter = app.quarter; $(".bottom").load("/details/" + product + "/" + quarter); } }; }).call(this, jQuery);
This code makes an Ajax request, taking into account the selection data, which is available through the arguments for the function. This, in turn, triggers either the “Performance” or the “Details” action handlers, which were listed in the “HomeController” controller.
This sums up the most important points in our setup. The compete code, along with a working project, is available for download here.