Updated product catalog demo

Added Entity Framework Core Example
Refactored code
This commit is contained in:
Jovan Popovic
2017-02-04 23:04:11 +01:00
parent 1675155de1
commit 81cd7166eb
23 changed files with 657 additions and 308 deletions
@@ -8,4 +8,5 @@ obj/*
*.lock.json
Properties/PublishProfiles/*
appsettings.Development.json
appsettings.Production.json
appsettings.Production.json
*.ndjson
@@ -35,10 +35,11 @@ namespace ProductCatalog.Controllers
string referer;
switch (Request.Query["page"])
{
case "index": referer = "/Home/Index"; break;
case "report1": referer = "/Home/Report1"; break;
case "report2": referer = "/Home/Report2"; break;
default: referer = "/index.html";break;
case "index": referer = "/index.html"; break;
case "report1": referer = "/report-pie.html"; break;
case "report2": referer = "/report-multibar.html"; break;
case "temporal": referer = "/temporal.html"; break;
default: referer = "/index.html"; break;
}
Response.Redirect(referer);
}
@@ -0,0 +1,50 @@
using Microsoft.AspNetCore.Mvc;
using ProductCatalog.Models;
using System;
using System.Linq;
namespace ProductCatalog.Controllers
{
public class ProductCatalogController : Controller
{
private ProductCatalogContext _context;
public ProductCatalogController (ProductCatalogContext context)
{
_context = context;
}
[HttpGet]
public IActionResult Index()
{
ViewData["page"] = "index";
return View(_context.Products.AsEnumerable());
}
// POST api/ProductCatalog/Add
public IActionResult Add(Product p)
{
try
{
_context.Products.Add(p);
_context.SaveChanges();
return Redirect("/ProductCatalog/Index");
} catch (Exception)
{
return Redirect("/ProductCatalog/Index");
}
}
public IActionResult Report1()
{
ViewData["page"] = "report1";
return View();
}
public IActionResult Report2()
{
ViewData["page"] = "report2";
return View();
}
}
}
@@ -0,0 +1,74 @@
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace ProductCatalog.Models
{
[Table("Product")]
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public string Color { get; set; }
public string Size { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public int CompanyID { get; set; }
[NotMapped]
public string[] Tags
{
get { return _Tags == null ? null : JsonConvert.DeserializeObject<string[]>(_Tags); }
set { _Tags = JsonConvert.SerializeObject(value); }
}
internal string _Tags { get; set; }
[NotMapped]
public Properties Data
{
get { return (this._Data == null) ? null : JsonConvert.DeserializeObject<Properties>(this._Data); }
set { _Data = JsonConvert.SerializeObject(value); }
}
internal string _Data { get; set; }
}
public class Properties
{
public string Type { get; set; }
public string MadeIn { get; set; }
}
public class ProductCatalogContext : DbContext
{
public ProductCatalogContext(DbContextOptions<ProductCatalogContext> options)
: base(options)
{ }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.Property(x => x.ProductId)
.HasDefaultValueSql("NEXT VALUE FOR ProductId");
modelBuilder.Entity<Product>()
.Property(b => b._Tags).HasColumnName("Tags");
modelBuilder.Entity<Product>()
.Property(b => b._Data).HasColumnName("Data");
}
public DbSet<Product> Products { get; set; }
}
}
@@ -11,7 +11,7 @@
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "Home/Index",
"launchUrl": "ProductCatalog/Index",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
@@ -19,7 +19,7 @@
"ProductCatalog": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "http://localhost:5000/Home/Index",
"launchUrl": "http://localhost:5000/ProductCatalog/Index",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
@@ -3,9 +3,11 @@ using Belgrade.SqlClient.SqlDb.Rls;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using ProductCatalog.Models;
using Serilog;
#if NET46
using Serilog.Sinks.MSSqlServer;
@@ -51,9 +53,10 @@ namespace ProductCatalog
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//string ConnString = Configuration.GetConnectionString("BelgradeDemo");
string ConnString = Configuration["ConnectionStrings:BelgradeDemo"];
services.AddDbContext<ProductCatalogContext>(options => options.UseSqlServer(new SqlConnection(ConnString)));
// Adding data access services/components.
services.AddTransient(
sp => new QueryPipe(new SqlConnection(ConnString))
@@ -65,7 +68,7 @@ namespace ProductCatalog
.AddRls("CompanyID", () => GetCompanyIdFromSession(sp))
);
// Add framework services.
//// Add framework services.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddLogging();
services.AddSession();
@@ -85,7 +88,7 @@ namespace ProductCatalog
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}");
template: "{controller=ProductCatalog}/{action=Index}");
});
}
@@ -6,66 +6,6 @@
</button>
</div>
<!-- JQuery slider for temporal -->
<div id="slider" class="pull-left" style="float:left;display:inline-block;margin-bottom:20px;margin-right:20px;"></div>
<!-- End JQuery slider for temporal -->
<!-- Bootstrap Modal -->
<div class="modal fade" id="modalEditProduct" tabindex="-1" role="dialog" aria-labelledby="myModalEditLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalEditLabel">Edit Product Details</h4>
</div>
<div class="modal-body">
<!-- Bootstrap form -->
<form id="EditProductForm">
<input type="hidden" id="ProductID" value="" />
<div class="form-group">
<label for="Name">Title</label>
<input type="text" class="form-control" name="Name" placeholder="Title">
</div>
<div class="form-group">
<label for="Color">Color</label>
<select class="form-control" name="Color">
<option value="">N/A</option>
<option value="White">White</option>
<option value="Silver">Silver</option>
<option value="Magenta">Magenta</option>
<option value="Red">Red</option>
<option value="Multi">Multi</option>
<option value="Black">Black</option>
</select>
</div>
<div class="form-group">
<label for="Price" class="field-label">Price</label>
<input type="text" id="Price" name="Price" class="form-control">
</div>
<div class="form-group">
<label for="Quantity" class="field-label">Quantity</label>
<input type="text" id="Quantity" name="Quantity" class="form-control">
</div>
<div class="form-group">
<h3 id="Company"></h3>
<p id="Address"></p>
<p>Contact: <span id="Email"></span>, <span id="Phone"></span></p>
</div>
<!-- Allow form submission with keyboard without duplicating the dialog button -->
<input type="submit" tabindex="-1" style="position:absolute; top:-1000px">
</form>
<!-- End Bootstrap form -->
</div>
<div class="modal-footer">
<button id="cancelEditButton" type="reset" class="btn btn-link" data-dismiss="modal">Close</button>
<button id="submitEditButton" type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-save"></span> Save</button>
</div>
</div>
</div>
</div>
<!-- End Bootstrap modal -->
<!-- Bootstrap Modal (ADD) -->
<div class="modal fade" id="modalAddProduct" tabindex="-1" role="dialog" aria-labelledby="myModalAddProductLabel">
<div class="modal-dialog" role="document">
@@ -77,8 +17,7 @@
<div class="modal-body">
<!-- Bootstrap form -->
<form id="AddProductForm">
<!--<input type="hidden" id="ProductID" value="" />-->
<form id="AddProductForm" action="~/ProductCatalog/Add" method="post">
<div class="form-group">
<label for="Name">Title</label>
<input type="text" class="form-control" name="Name" placeholder="Title">
@@ -100,8 +39,16 @@
<input type="text" name="Price" class="form-control">
</div>
<div class="form-group">
<label for="Quantity" class="field-label">Quantity</label>
<input type="text" name="Quantity" class="form-control">
<label for="Data.MadeIn" class="field-label">Made In</label>
<input type="text" name="Data.MadeIn" class="form-control">
</div>
<div class="form-group">
<label for="Tags[]">Tags</label>
<select class="form-control" name="Tags[]" multiple>
<option value="sales">Sales</option>
<option value="promo">Promo</option>
<option value="new">New</option>
</select>
</div>
<div class="form-group">
<label for="Company">Company</label>
@@ -117,7 +64,7 @@
</div>
<div class="modal-footer">
<button id="cancelAddButton" type="reset" class="btn btn-link" data-dismiss="modal">Close</button>
<button id="submitAddButton" type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-save"></span> Save</button>
<button id="submitAddButton2" type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-save"></span> Save</button>
</div>
</div>
</div>
@@ -127,19 +74,27 @@
<table id="example" class="table table-striped table-bordered" cellspacing="0" width="100%">
<thead>
<tr>
<th></th>
<th>Product</th>
<th>Color</th>
<th>Price</th>
<th>Quantity</th>
<th>Made in</th>
<th>Tags</th>
<th>Edit</th>
<th>Delete</th>
<th>Restore</th>
</tr>
</thead>
<tbody></tbody>
<tbody>
@foreach (var product in Model)
{
<tr>
<td>@product.Name</td>
<td>@product.Color</td>
<td>@product.Price</td>
<td>@product.Quantity</td>
<td>@product.Data.MadeIn</td>
<td>@(product.Tags==null?string.Empty:string.Join(", ", product.Tags))</td>
</tr>
}
</tbody>
</table>
<!-- End JQuery DataTable -->
</div>
@@ -158,6 +113,12 @@
<script src="~/media/js/lib/jquery.html-template.js"></script>
<script src="~/media/js/lib/jquery.serializejson.js"></script>
<script src="~/media/js/lib/toastr.min.js"></script>
<script src="~/media/js/products.js"></script>
<script src="~/media/js/products-temporal.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$('#example').DataTable();
});
</script>
<script src="~/media/js/products-crud.js"></script>
}
@@ -1,5 +1,4 @@
<h1>Products<span id="snapshot"></span></h1>
<div class="testBlock"><svg id="test1"></svg></div>
<div class="testBlock"><svg id="test1"></svg></div>
<div class="testBlock"><svg id="test2"></svg></div>
@section styles {
@@ -29,8 +28,8 @@
<script src="~/media/js/lib/nvd3/d3.min.js" charset="utf-8"></script>
<script src="~/media/js/lib/nvd3/nv.d3.min.js"></script>
<script>
//$.ajax('/data/nvd3-pie.js', { "dataType": "json" })
$.ajax('/api/Product/Report1', { "dataType": "json" })
$.ajax('/data/nvd3-pie.js', { "dataType": "json" })
//$.ajax('/api/Product/Report1', { "dataType": "json" })
.done(function (testdata) {
var width = 300;
@@ -52,24 +51,12 @@
.attr('height', height)
.call(chart);
// LISTEN TO CLICK EVENTS ON THE PIE CONTAINER
// chart.dispatch.on('chartClick', function() {
// code...
// });
// LISTEN TO CLICK EVENTS ON THE SLICES OF THE PIE
// chart.dispatch.on('elementClick', function() {
// code...
// });
// OTHER EVENTS DISPATCHED BY THE PIE INCLUDE: elementDblClick, elementMouseover, elementMouseout, elementMousemove, renderEnd
// see nv.models.pie
return chart;
});
nv.addGraph(function () {
var chart = nv.models.pie()
.x(function (d) { return d.key; })
.x(function (d) { return d.x; })
.y(function (d) { return d.y; })
.width(width)
.height(height)
@@ -90,8 +77,4 @@
});
</script>
}
}
@@ -28,8 +28,8 @@
<script src="~/media/js/lib/nvd3/d3.min.js" charset="utf-8"></script>
<script src="~/media/js/lib/nvd3/nv.d3.min.js"></script>
<script>
//$.ajax('data/nvd3-multibar.js', { "dataType": "json" })
$.ajax('/api/Product/Report2', { "dataType": "json" })
$.ajax('/data/nvd3-multibar.js', { "dataType": "json" })
//$.ajax('/api/Product/Report2', { "dataType": "json" })
.done(function (exampleData) {
nv.addGraph(function () {
@@ -22,14 +22,13 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Product Catalog Demo</a>
<a class="navbar-brand" href="/index.html">Product Catalog Demo</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="index"><a href="~/Home/Index">Products</a></li>
<li class="report1"><a href="~/Home/Report1">Pie chart</a></li>
<li class="report2"><a href="~/Home/Report2">Bar chart</a></li>
<li class="index"><a href="~/ProductCatalog/Index">Product list</a></li>
<li class="report1"><a href="~/ProductCatalog/Report1">Products by color</a></li>
<li class="report2"><a href="~/ProductCatalog/Report2">Quantity by company/color</a></li>
</ul>
<ul class="nav navbar-nav pull-right">
<li class="dropdown pull-right">
@@ -52,6 +51,7 @@
<script src="~/media/js/lib/jquery.js"></script>
<script>$("li." + $("body")[0].id).addClass("active");</script>
<script src="~/media/js/lib/Bootstrap.js"></script>
<script src="~/media/js/rls.js"></script>
@RenderSection("scripts", required: false)
@@ -1,6 +1,6 @@
{
{
"Logging": {
"IncludeScopes": false,
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
@@ -8,6 +8,6 @@
}
},
"ConnectionStrings": {
"BelgradeDemo": "Server=.;Database=ProductCatalog;Integrated Security=true"
"BelgradeDemo": "Server=.\\SQLEXPRESS;Database=ProductCatalog;Integrated Security=true"
}
}
}
@@ -6,6 +6,7 @@
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
"Microsoft.AspNetCore.Session": "1.0.0",
"Microsoft.AspNetCore.StaticFiles": "1.0.0",
"Microsoft.EntityFrameworkCore.SqlServer": "1.0.1",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
"Microsoft.Extensions.Configuration.FileExtensions": "1.0.0",
"Microsoft.Extensions.Configuration.Json": "1.0.0",
@@ -1,7 +1,13 @@
SELECT
SELECT * FROM Logs
SELECT JSON_VALUE(LogEvent, '$.Properties.RequestPath') as Url, *
FROM Logs
WHERE Level = 'Error'
SELECT
JSON_VALUE(LogEvent, '$.Properties.RequestPath') as Url,
AVG( CAST(JSON_VALUE(LogEvent, '$.Properties.ElapsedMilliseconds') as float) ) as milliseconds
FROM Logs
WHERE JSON_VALUE(LogEvent, '$.Properties.RequestPath') IS NOT NULL
AND Timestamp BETWEEN '2017-01-09' AND '2017-01-10'
--AND Timestamp BETWEEN '2017-01-09' AND '2017-01-10'
GROUP BY JSON_VALUE(LogEvent, '$.Properties.RequestPath')
@@ -1 +1,25 @@
[{ "key": "A Datum Corporation", "values": [{ "x": "Black", "y": 31.4900 }, { "x": "Magenta", "y": 100.0000 }, { "x": "Silver", "y": 359.9900 }] }, { "key": "Consolidated Messenger", "values": [{ "x": "Magenta", "y": 28.9900 }, { "x": "Red", "y": 41.9900 }, { "x": "White", "y": 120.9900 }] }, { "key": "Contoso, Ltd.", "values": [{ "x": "White", "y": 560.9900 }, { "x": "Magenta", "y": 15.9900 }, { "x": "Black", "y": 299.0200 }] }]
[
{
"key": "A Datum Corporation",
"values": [
{ "x": "Black", "y": 31.4900 },
{ "x": "Magenta", "y": 100.0000 },
{ "x": "Silver", "y": 359.9900 }
]
},
{
"key": "Consolidated Messenger",
"values": [
{ "x": "Magenta", "y": 28.9900 },
{ "x": "Red", "y": 41.9900 },
{ "x": "White", "y": 120.9900 }
]
}, {
"key": "Contoso, Ltd.",
"values": [
{ "x": "White", "y": 560.9900 },
{ "x": "Magenta", "y": 15.9900 },
{ "x": "Black", "y": 299.0200 }
]
}
]
@@ -1,9 +1,9 @@
[
{ "key": "One", "y": 5 },
{ "key": "Two", "y": 2 },
{ "key": "Three", "y": 9 },
{ "key": "Four", "y": 7 },
{ "key": "Five", "y": 4 },
{ "key": "Six", "y": 3 },
{ "key": "Seven", "y": 0.5 }
{ "x": "Red", "y": 5 },
{ "x": "Silver", "y": 2 },
{ "x": "Black", "y": 9 },
{ "x": "Red", "y": 7 },
{ "x": "Magenta", "y": 4 },
{ "x": "White", "y": 3 },
{ "x": "Blue", "y": 0.5 }
]
@@ -23,9 +23,9 @@
<script src="media/js/lib/jquery.serializejson.js"></script>
<script src="media/js/lib/toastr.min.js"></script>
<script src="media/js/products.js"></script>
<script src="media/js/products-temporal.js"></script>
<script type="text/javascript" src="media/js/products.js"></script>
<script src="media/js/products-crud.js"></script>
<script src="media/js/rls.js"></script>
</head>
<body>
<!-- Static navbar -->
@@ -38,23 +38,23 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Product Catalog Demo</a>
<a class="navbar-brand" href="/ProductCatalog/Index">Product Catalog Demo</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Products</a></li>
<li><a href="report-pie.html">Pie chart</a></li>
<li><a href="report-multibar.html">Bar chart</a></li>
<li class="active"><a href="#">Product List</a></li>
<li><a href="report-pie.html">Products by color</a></li>
<li><a href="report-multibar.html">Quantity by company/color</a></li>
<li><a href="temporal.html">Product History</a></li>
</ul>
<ul class="nav navbar-nav pull-right">
<li class="dropdown pull-right">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Current user: <span class="UserGreeting">Admin</span><span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="/api/Company/login?id=0" class="user-role">Admin</a></li>
<li><a href="/api/Company/login?id=1" class="user-role">A Datum Corporation</a></li>
<li><a href="/api/Company/login?id=2" class="user-role">Contoso, Ltd.</a></li>
<li><a href="/api/Company/login?id=3" class="user-role">Consolidated Messenger</a></li>
<li><a href="/api/Company/login?id=0&page=index" class="user-role">Admin</a></li>
<li><a href="/api/Company/login?id=1&page=index" class="user-role">A Datum Corporation</a></li>
<li><a href="/api/Company/login?id=2&page=index" class="user-role">Contoso, Ltd.</a></li>
<li><a href="/api/Company/login?id=3&page=index" class="user-role">Consolidated Messenger</a></li>
</ul>
</li>
</ul>
@@ -196,7 +196,6 @@
<table id="example" class="table table-striped table-bordered" cellspacing="0" width="100%">
<thead>
<tr>
<th></th>
<th>Product</th>
<th>Color</th>
<th>Price</th>
@@ -205,7 +204,6 @@
<th>Tags</th>
<th>Edit</th>
<th>Delete</th>
<th>Restore</th>
</tr>
</thead>
<tbody></tbody>
@@ -0,0 +1,114 @@
ROOT_API_URL = "/api/Product/";
// ProductController is an object that contains actions the will be executed on
// get, save (create or update), delete
var ProductController =
function ($table, $modal, $modalAddProduct) {
return {
getProduct: function (productID) {
$.ajax({ url: ROOT_API_URL + productID, cache: false })
.done( function (json) {
$modal.loadJSON(json);
})
.fail(function () {
toastr.error('An error occured while trying to get the product.');
});
},
saveProduct: function (productID, product) {
$.ajax({
contentType: 'application/json',
method: (productID === null) ? "POST" : "PUT",
url: ROOT_API_URL + (productID||""),
processData: false,
data: product,
}).fail(function (msg) {
toastr.error('An error occured while trying to save the product.');
}).done(function () {
toastr.success("Product is successfully saved!");
$table.ajax.reload(null, false);
if (productID === null)
$modalAddProduct.modal('hide');
else
$modal.modal('hide');
});
},
deleteProduct: function (productID) {
$.ajax({
method: "DELETE",
url: ROOT_API_URL + productID
}).fail(function (msg) {
toastr.error('An error occured while trying to delete the product.', 'Product cannot be deleted!');
}).done(function () {
toastr.success("Product is successfully deleted!");
$table.ajax.reload(null, false);
});
}
}
};
$(document).ready(function () {
var $table = $('#example').DataTable();
// Bootstrap modal setup
$modalEditProduct = $('#modalEditProduct');
$modalEditProduct.on('hide.bs.modal', function () {
$(this).find("input[type!=checkbox],textarea,select").val('').end();
$(this).find("input:checkbox").prop('checked', false);
});
$("#cancelEditButton", $modalEditProduct).on("click", function () {
$modalEditProduct.modal('hide');
});
$modalAddProduct = $('#modalAddProduct');
$modalAddProduct.on('hide.bs.modal', function () {
$(this).find("input[type!=checkbox],textarea,select").val('').end();
$(this).find("input:checkbox").prop('checked', false);
});
$("#cancelAddButton", $modalAddProduct).on("click", function () {
$modalAddProduct.modal('hide');
});
// end modal setup
var ctrl = ProductController($table, $modalEditProduct, $modalAddProduct);
$table.on("click", "button.edit",
function () {
ctrl.getProduct(this.attributes["data-id"].value);
});
$table.on("click", "button.delete",
function () {
ctrl.deleteProduct(this.attributes["data-id"].value);
});
$('body').on("click", "#submitEditButton",
function (e) {
e.preventDefault();
var $form = $("#EditProductForm");
var productId = $("#ProductID", $form).val();
var product = JSON.stringify($form.serializeJSON({ checkboxUncheckedValue: "false", parseAll: true }));
ctrl.saveProduct(productId, product);
});
$('body').on("click", "#submitAddButton",
function (e) {
e.preventDefault();
var $form = $("#AddProductForm");
var product = JSON.stringify($form.serializeJSON({ checkboxUncheckedValue: "false", parseAll: true }));
ctrl.saveProduct(null, product);
});
$('body').on("click", "#submitAddButton2",
function (e) {
e.preventDefault();
var $form = $("#AddProductForm");
$form.submit();
});
});
@@ -1,4 +1,49 @@
$(function () {
ROOT_API_URL = "/api/Product/";
$(document).ready(function () {
// DataTable setup
$('#example').DataTable({
"ajax": ROOT_API_URL + "temporal",
"columns": [
{ "className": 'details-control', "visible": true, "sortable": false, "searchable": false, "defaultContent": "" },
{ "data": "Name" },
{ "data": "Color", "defaultContent": "" },
{ "data": "Price", sType: 'numeric', "defaultContent": "" },
{ "data": "Quantity", "visible": true, "defaultContent": "" },
{ "data": "MadeIn", "visible": true, "defaultContent": "" },
{ "data": "Tags", "visible": true, "defaultContent": "" },
{
"data": "ProductID",
"sortable": false,
"searchable": false,
"render": function (data) {
return '<button data-id="' + data + '" class="btn btn-primary btn-sm edit" data-toggle="modal" data-target="#modalEditProduct"><span class="glyphicon glyphicon-edit"></span> Edit</button>';
}
},
{
"data": "ProductID",
"sortable": false,
"searchable": false,
"render": function (data) {
return '<button data-id="' + data + '" class="btn btn-danger btn-sm delete"><span class="glyphicon glyphicon-remove"></span> Delete</button>';
}
},
{
"data": "ProductID",
"visible": false,
"sortable": false,
"searchable": false,
"render": function (data, type, full) {
return '<a href="' + ROOT_API_URL + 'restore?ProductID=' + data + '&DateModified=' + full.DateModified + '" class="restore btn btn-success btn-sm delete"><span class="glyphicon glyphicon-floppy-open"></span> Restore</button>';
}
}
]
});// end DataTable setup
});
$(function () {
var $table = $('#example').DataTable();
// Add event listener for opening and closing details
@@ -1,62 +1,11 @@
ROOT_API_URL = "/api/Product/";
// ProductController is an object that contains actions the will be executed on
// get, save (create or update), delete
var ProductController =
function ($table, $modal, $modalAddProduct) {
return {
getProduct: function (productID) {
$.ajax({ url: ROOT_API_URL + productID, cache: false })
.done( function (json) {
$modal.loadJSON(json);
})
.fail(function () {
toastr.error('An error occured while trying to get the product.');
});
},
saveProduct: function (productID, product) {
$.ajax({
contentType: 'application/json',
method: (productID === null) ? "POST" : "PUT",
url: ROOT_API_URL + (productID||""),
processData: false,
data: product,
}).fail(function (msg) {
toastr.error('An error occured while trying to save the product.');
}).done(function () {
toastr.success("Product is successfully saved!");
$table.ajax.reload(null, false);
if (productID === null)
$modalAddProduct.modal('hide');
else
$modal.modal('hide');
});
},
deleteProduct: function (productID) {
$.ajax({
method: "DELETE",
url: ROOT_API_URL + productID
}).fail(function (msg) {
toastr.error('An error occured while trying to delete the product.', 'Product cannot be deleted!');
}).done(function () {
toastr.success("Product is successfully deleted!");
$table.ajax.reload(null, false);
});
}
}
};
$(document).ready(function () {
// DataTable setup
var $table = $('#example').DataTable({
"ajax": ROOT_API_URL + 'temporal',
$('#example').DataTable({
"ajax": ROOT_API_URL,
"columns": [
{ "className": 'details-control', "visible": true, "sortable": false, "searchable": false, "defaultContent": "" },
{ "data": "Name" },
{ "data": "Color", "defaultContent": "" },
{ "data": "Price", sType: 'numeric', "defaultContent": "" },
@@ -78,78 +27,8 @@ $(document).ready(function () {
"render": function (data) {
return '<button data-id="' + data + '" class="btn btn-danger btn-sm delete"><span class="glyphicon glyphicon-remove"></span> Delete</button>';
}
},
{
"data": "ProductID",
"visible": false,
"sortable": false,
"searchable": false,
"render": function (data, type, full) {
return '<a href="' + ROOT_API_URL + 'restore?ProductID=' + data + '&DateModified=' + full.DateModified + '" class="restore btn btn-success btn-sm delete"><span class="glyphicon glyphicon-floppy-open"></span> Restore</button>';
}
}
]
});// end DataTable setup
// Bootstrap modal setup
$modalEditProduct = $('#modalEditProduct');
$modalEditProduct.on('hide.bs.modal', function () {
$(this).find("input[type!=checkbox],textarea,select").val('').end();
$(this).find("input:checkbox").prop('checked', false);
});
$("#cancelEditButton", $modalEditProduct).on("click", function () {
$modalEditProduct.modal('hide');
});
$modalAddProduct = $('#modalAddProduct');
$modalAddProduct.on('hide.bs.modal', function () {
$(this).find("input[type!=checkbox],textarea,select").val('').end();
$(this).find("input:checkbox").prop('checked', false);
});
$("#cancelAddButton", $modalAddProduct).on("click", function () {
$modalAddProduct.modal('hide');
});
// end modal setup
var ctrl = ProductController($table, $modalEditProduct, $modalAddProduct);
$table.on("click", "button.edit",
function () {
ctrl.getProduct(this.attributes["data-id"].value);
});
$table.on("click", "button.delete",
function () {
ctrl.deleteProduct(this.attributes["data-id"].value);
});
$('body').on("click", "#submitEditButton",
function (e) {
e.preventDefault();
var $form = $("#EditProductForm");
var productId = $("#ProductID", $form).val();
var product = JSON.stringify($form.serializeJSON({ checkboxUncheckedValue: "false", parseAll: true }));
ctrl.saveProduct(productId, product);
});
$('body').on("click", "#submitAddButton",
function (e) {
e.preventDefault();
var $form = $("#AddProductForm");
var product = JSON.stringify($form.serializeJSON({ checkboxUncheckedValue: "false", parseAll: true }));
ctrl.saveProduct(null, product);
});
$.ajax("/api/Company").done(function (json) {
$("#CompanyList").loadJSON(json);
});
$("li a.user-role").on("click", function () {
localStorage.User = $(this).text();
});
$("span.UserGreeting").text(localStorage.User);
});
@@ -0,0 +1,12 @@
$(document).ready(function () {
$.ajax("/api/Company").done(function (json) {
$("#CompanyList").loadJSON(json);
});
$("li a.user-role").on("click", function () {
localStorage.User = $(this).text();
});
$("span.UserGreeting").text(localStorage.User);
});
@@ -5,9 +5,10 @@
<link href="media/css/Bootstrap.css" rel="stylesheet" />
<link href="media/js/lib/nvd3/nv.d3.min.css" rel="stylesheet" />
<script src="media/js/lib/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.2/d3.min.js" charset="utf-8"></script>
<script src="media/js/lib/nvd3/d3.min.js"></script>
<script src="media/js/lib/nvd3/nv.d3.min.js"></script>
<script src="media/js/lib/Bootstrap.js"></script>
<script src="media/js/rls.js"></script>
<style>
text {
font: 12px sans-serif;
@@ -36,22 +37,23 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Product Catalog Demo</a>
<a class="navbar-brand" href="/ProductCatalog/Index">Product Catalog Demo</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="index.html">Products</a></li>
<li><a href="report-pie.html">Pie chart</a></li>
<li class="active"><a href="#">Bar chart</a></li>
<li><a href="index.html">Product List</a></li>
<li><a href="report-pie.html">Products by color</a></li>
<li class="active"><a href="#">Quantity by company/color</a></li>
<li><a href="temporal.html">Product History</a></li>
</ul>
<ul class="nav navbar-nav pull-right">
<li class="dropdown pull-right">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Current user: <span class="UserGreeting">Admin</span><span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="/api/Company/login?id=0" class="user-role">Admin</a></li>
<li><a href="/api/Company/login?id=1" class="user-role">A Datum Corporation</a></li>
<li><a href="/api/Company/login?id=2" class="user-role">Contoso, Ltd.</a></li>
<li><a href="/api/Company/login?id=3" class="user-role">Consolidated Messenger</a></li>
<li><a href="/api/Company/login?id=0&page=report2" class="user-role">Admin</a></li>
<li><a href="/api/Company/login?id=1&page=report2" class="user-role">A Datum Corporation</a></li>
<li><a href="/api/Company/login?id=2&page=report2" class="user-role">Contoso, Ltd.</a></li>
<li><a href="/api/Company/login?id=3&page=report2" class="user-role">Consolidated Messenger</a></li>
</ul>
</li>
</ul>
@@ -71,7 +73,7 @@
nv.addGraph(function () {
var chart = nv.models.multiBarChart()
.height(600)
.height(400)
.reduceXTicks(true) //If 'false', every single x-axis tick label will be rendered.
.rotateLabels(0) //Angle to rotate x-axis labels.
.showControls(false) //Allow user to switch between 'Grouped' and 'Stacked' mode.
@@ -89,20 +91,7 @@
return chart;
});
});
//Generate some nice data.
function exampleData() {
return stream_layers(3, 10 + Math.random() * 100, .1).map(function (data, i) {
return {
key: 'Stream #' + i,
values: data
};
});
}
</script>
</body>
</html>
@@ -5,9 +5,10 @@
<link href="media/css/Bootstrap.css" rel="stylesheet" />
<link href="media/js/lib/nvd3/nv.d3.min.css" rel="stylesheet" />
<script src="media/js/lib/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.2/d3.min.js" charset="utf-8"></script>
<script src="media/js/lib/nvd3/d3.min.js"></script>
<script src="media/js/lib/nvd3/nv.d3.min.js"></script>
<script src="media/js/lib/Bootstrap.js"></script>
<script src="media/js/rls.js"></script>
<style>
text {
font: 12px sans-serif;
@@ -39,22 +40,23 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Product Catalog Demo</a>
<a class="navbar-brand" href="/ProductCatalog/Index">Product Catalog Demo</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="index.html">Products</a></li>
<li class="active"><a href="#">Pie chart</a></li>
<li><a href="report-multibar.html">Bar chart</a></li>
<li><a href="index.html">Product List</a></li>
<li class="active"><a href="#">Products by color</a></li>
<li><a href="report-multibar.html">Quantity by company/color</a></li>
<li><a href="temporal.html">Product History</a></li>
</ul>
<ul class="nav navbar-nav pull-right">
<li class="dropdown pull-right">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Current user: <span class="UserGreeting">Admin</span><span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="/api/Company/login?id=0" class="user-role">Admin</a></li>
<li><a href="/api/Company/login?id=1" class="user-role">A Datum Corporation</a></li>
<li><a href="/api/Company/login?id=2" class="user-role">Contoso, Ltd.</a></li>
<li><a href="/api/Company/login?id=3" class="user-role">Consolidated Messenger</a></li>
<li><a href="/api/Company/login?id=0&page=report1" class="user-role">Admin</a></li>
<li><a href="/api/Company/login?id=1&page=report1" class="user-role">A Datum Corporation</a></li>
<li><a href="/api/Company/login?id=2&page=report1" class="user-role">Contoso, Ltd.</a></li>
<li><a href="/api/Company/login?id=3&page=report1" class="user-role">Consolidated Messenger</a></li>
</ul>
</li>
</ul>
@@ -92,18 +94,6 @@
.attr('height', height)
.call(chart);
// LISTEN TO CLICK EVENTS ON THE PIE CONTAINER
// chart.dispatch.on('chartClick', function() {
// code...
// });
// LISTEN TO CLICK EVENTS ON THE SLICES OF THE PIE
// chart.dispatch.on('elementClick', function() {
// code...
// });
// OTHER EVENTS DISPATCHED BY THE PIE INCLUDE: elementDblClick, elementMouseover, elementMouseout, elementMousemove, renderEnd
// @see nv.models.pie
return chart;
});
@@ -0,0 +1,218 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="shortcut icon" type="image/ico" href="media/images/favicon.ico">
<meta name="viewport" content="initial-scale=1.0, maximum-scale=2.0">
<title>Products</title>
<link href="media/css/jquery-ui/jquery-ui.css" rel="stylesheet" />
<link href="media/css/Bootstrap.css" rel="stylesheet" />
<link href="media/css/dataTables.bootstrap.css" rel="stylesheet" />
<link href="media/css/toastr.min.css" rel="stylesheet" />
<link href="media/css/products.css" rel="stylesheet" />
<script src="media/js/lib/jquery.js"></script>
<script src="media/js/lib/jquery-ui.js"></script>
<script src="media/js/lib/Bootstrap.js"></script>
<script src="media/js/lib/jquery.dataTables.js"></script>
<script src="media/js/lib/jquery.dataTables.Bootstrap.js"></script>
<script src="media/js/lib/jquery.html-template.js"></script>
<script src="media/js/lib/jquery.serializejson.js"></script>
<script src="media/js/lib/toastr.min.js"></script>
<script src="media/js/products-temporal.js"></script>
<script src="media/js/products-crud.js"></script>
<script src="media/js/rls.js"></script>
</head>
<body>
<!-- Static navbar -->
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/ProductCatalog/Index">Product Catalog Demo</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="index.html">Product List</a></li>
<li><a href="report-pie.html">Products by color</a></li>
<li><a href="report-multibar.html">Quantity by company/color</a></li>
<li class="active"><a href="#">Product History</a></li>
</ul>
<ul class="nav navbar-nav pull-right">
<li class="dropdown pull-right">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Current user: <span class="UserGreeting">Admin</span><span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="/api/Company/login?id=0&page=temporal" class="user-role">Admin</a></li>
<li><a href="/api/Company/login?id=1&page=temporal" class="user-role">A Datum Corporation</a></li>
<li><a href="/api/Company/login?id=2&page=temporal" class="user-role">Contoso, Ltd.</a></li>
<li><a href="/api/Company/login?id=3&page=temporal" class="user-role">Consolidated Messenger</a></li>
</ul>
</li>
</ul>
</div><!--/.nav-collapse -->
</div><!--/.container-fluid -->
</nav>
<!-- End Static navbar -->
<div class="container">
<div class="clearfix">
<h1 class="pull-left">Products<span id="snapshot"></span></h1>
<button id="addProduct" type="button" class="btn btn-primary pull-right" data-toggle="modal" data-target="#modalAddProduct">
<span class="glyphicon glyphicon-plus"></span> Add
</button>
</div>
<!-- JQuery slider for temporal -->
<div id="slider" class="pull-left" style="float:left;display:inline-block;margin-bottom:20px;margin-right:20px;"></div>
<!-- End JQuery slider for temporal -->
<!-- Bootstrap Modal -->
<div class="modal fade" id="modalEditProduct" tabindex="-1" role="dialog" aria-labelledby="myModalEditLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalEditLabel">Edit Product Details</h4>
</div>
<div class="modal-body">
<!-- Bootstrap form -->
<form id="EditProductForm">
<input type="hidden" id="ProductID" value="" />
<div class="form-group">
<label for="Name">Title</label>
<input type="text" class="form-control" name="Name" placeholder="Title">
</div>
<div class="form-group">
<label for="Color">Color</label>
<select class="form-control" name="Color">
<option value="">N/A</option>
<option value="White">White</option>
<option value="Silver">Silver</option>
<option value="Magenta">Magenta</option>
<option value="Red">Red</option>
<option value="Multi">Multi</option>
<option value="Black">Black</option>
</select>
</div>
<div class="form-group">
<label for="Price" class="field-label">Price</label>
<input type="text" id="Price" name="Price" class="form-control">
</div>
<div class="form-group">
<label for="Quantity" class="field-label">Quantity</label>
<input type="text" id="Quantity" name="Quantity" class="form-control">
</div>
<div class="form-group">
<h3 id="Company"></h3>
<p id="Address"></p>
<p>Contact: <span id="Email"></span>, <span id="Phone"></span></p>
</div>
<!-- Allow form submission with keyboard without duplicating the dialog button -->
<input type="submit" tabindex="-1" style="position:absolute; top:-1000px">
</form>
<!-- End Bootstrap form -->
</div>
<div class="modal-footer">
<button id="cancelEditButton" type="reset" class="btn btn-link" data-dismiss="modal">Close</button>
<button id="submitEditButton" type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-save"></span> Save</button>
</div>
</div>
</div>
</div>
<!-- End Bootstrap modal -->
<!-- Bootstrap Modal (ADD) -->
<div class="modal fade" id="modalAddProduct" tabindex="-1" role="dialog" aria-labelledby="myModalAddProductLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalAddProductLabel">Add new product</h4>
</div>
<div class="modal-body">
<!-- Bootstrap form -->
<form id="AddProductForm">
<!--<input type="hidden" id="ProductID" value="" />-->
<div class="form-group">
<label for="Name">Title</label>
<input type="text" class="form-control" name="Name" placeholder="Title">
</div>
<div class="form-group">
<label for="Color">Color</label>
<select class="form-control" name="Color">
<option value="">N/A</option>
<option value="White">White</option>
<option value="Silver">Silver</option>
<option value="Magenta">Magenta</option>
<option value="Red">Red</option>
<option value="Multi">Multi</option>
<option value="Black">Black</option>
</select>
</div>
<div class="form-group">
<label for="Price" class="field-label">Price</label>
<input type="text" name="Price" class="form-control">
</div>
<div class="form-group">
<label for="Quantity" class="field-label">Quantity</label>
<input type="text" name="Quantity" class="form-control">
</div>
<div class="form-group">
<label for="Company">Company</label>
<select class="form-control" name="CompanyID" id="CompanyList">
</select>
</div>
<!-- Allow form submission with keyboard without duplicating the dialog button -->
<input type="submit" tabindex="-1" style="position:absolute; top:-1000px">
</form>
<!-- End Bootstrap form -->
</div>
<div class="modal-footer">
<button id="cancelAddButton" type="reset" class="btn btn-link" data-dismiss="modal">Close</button>
<button id="submitAddButton" type="submit" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-save"></span> Save</button>
</div>
</div>
</div>
</div>
<!-- End Bootstrap modal -->
<!-- JQuery DataTable -->
<table id="example" class="table table-striped table-bordered" cellspacing="0" width="100%">
<thead>
<tr>
<th></th>
<th>Product</th>
<th>Color</th>
<th>Price</th>
<th>Quantity</th>
<th>Made in</th>
<th>Tags</th>
<th>Edit</th>
<th>Delete</th>
<th>Restore</th>
</tr>
</thead>
<tbody></tbody>
</table>
<!-- End JQuery DataTable -->
</div>
</body>
</html>