From 7fb02eb8f16be495f0edd625635b5a93f0e0d466 Mon Sep 17 00:00:00 2001 From: Polina Tyureva Date: Tue, 1 Jul 2025 15:12:58 +0400 Subject: [PATCH 01/12] update example --- Reporting-React-Print-Without-Preview.sln | 25 - .../Controllers/HomeController.cs | 184 +- ServerApp/Controllers/ReportingControllers.cs | 45 + ServerApp/Data/ReportDbContext.cs | 63 + .../Data/nwind.db | Bin .../Data/nwind.json | 2010 ++++++++--------- ServerApp/Data/reportsData.db | Bin 0 -> 32768 bytes .../Model/ExportModel.cs | 24 +- ServerApp/Pages/Error.cshtml | 23 + .../Pages/Error.cshtml.cs | 54 +- ServerApp/Pages/_ViewImports.cshtml | 3 + .../PredefinedReports/ReportsFactory.cs | 32 +- .../PredefinedReports/TestReport.Designer.cs | 148 +- .../PredefinedReports/TestReport.cs | 26 +- .../PredefinedReports/TestReport.repx | 156 +- ServerApp/PredefinedReports/TestReport.resx | 123 + ServerApp/Program.cs | 71 + ServerApp/ServerApp.csproj | 40 + ServerApp/ServerApp.sln | 24 + .../Services/ReportStorageWebExtension.cs | 192 +- ServerApp/appsettings.Development.json | 10 + ServerApp/appsettings.json | 12 + .../.gitignore | 232 -- .../ClientApp/.gitignore | 23 - .../ClientApp/package.json | 49 - .../ClientApp/public/favicon.ico | Bin 32038 -> 0 bytes .../ClientApp/public/index.html | 44 - .../ClientApp/public/manifest.json | 15 - .../ClientApp/src/App.test.js | 13 - .../ClientApp/src/custom.css | 14 - .../ClientApp/src/index.js | 18 - .../Controllers/ReportingControllers.cs | 23 - .../Pages/Error.cshtml | 26 - .../Pages/_ViewImports.cshtml | 3 - .../Program.cs | 26 - .../Startup.cs | 99 - .../appsettings.Development.json | 9 - .../appsettings.json | 13 - ...leReactReportingPrintWithoutPreview.csproj | 60 - react-app/.gitignore | 24 + react-app/README.md | 12 + react-app/eslint.config.js | 29 + react-app/index.html | 13 + react-app/package.json | 29 + react-app/public/vite.svg | 1 + react-app/src/App.css | 42 + .../src/App.js => react-app/src/App.jsx | 37 +- react-app/src/assets/react.svg | 1 + .../src/components/HomeComponent.jsx | 92 +- react-app/src/index.css | 68 + react-app/src/main.jsx | 14 + .../src/registerServiceWorker.js | 216 +- react-app/vite.config.js | 18 + 53 files changed, 2247 insertions(+), 2281 deletions(-) delete mode 100644 Reporting-React-Print-Without-Preview.sln rename {dxSampleReactReportingPrintWithoutPreview => ServerApp}/Controllers/HomeController.cs (92%) create mode 100644 ServerApp/Controllers/ReportingControllers.cs create mode 100644 ServerApp/Data/ReportDbContext.cs rename {dxSampleReactReportingPrintWithoutPreview => ServerApp}/Data/nwind.db (100%) rename {dxSampleReactReportingPrintWithoutPreview => ServerApp}/Data/nwind.json (96%) create mode 100644 ServerApp/Data/reportsData.db rename {dxSampleReactReportingPrintWithoutPreview => ServerApp}/Model/ExportModel.cs (72%) create mode 100644 ServerApp/Pages/Error.cshtml rename {dxSampleReactReportingPrintWithoutPreview => ServerApp}/Pages/Error.cshtml.cs (67%) create mode 100644 ServerApp/Pages/_ViewImports.cshtml rename {dxSampleReactReportingPrintWithoutPreview => ServerApp}/PredefinedReports/ReportsFactory.cs (81%) rename {dxSampleReactReportingPrintWithoutPreview => ServerApp}/PredefinedReports/TestReport.Designer.cs (94%) rename {dxSampleReactReportingPrintWithoutPreview => ServerApp}/PredefinedReports/TestReport.cs (68%) rename {dxSampleReactReportingPrintWithoutPreview => ServerApp}/PredefinedReports/TestReport.repx (98%) create mode 100644 ServerApp/PredefinedReports/TestReport.resx create mode 100644 ServerApp/Program.cs create mode 100644 ServerApp/ServerApp.csproj create mode 100644 ServerApp/ServerApp.sln rename {dxSampleReactReportingPrintWithoutPreview => ServerApp}/Services/ReportStorageWebExtension.cs (95%) create mode 100644 ServerApp/appsettings.Development.json create mode 100644 ServerApp/appsettings.json delete mode 100644 dxSampleReactReportingPrintWithoutPreview/.gitignore delete mode 100644 dxSampleReactReportingPrintWithoutPreview/ClientApp/.gitignore delete mode 100644 dxSampleReactReportingPrintWithoutPreview/ClientApp/package.json delete mode 100644 dxSampleReactReportingPrintWithoutPreview/ClientApp/public/favicon.ico delete mode 100644 dxSampleReactReportingPrintWithoutPreview/ClientApp/public/index.html delete mode 100644 dxSampleReactReportingPrintWithoutPreview/ClientApp/public/manifest.json delete mode 100644 dxSampleReactReportingPrintWithoutPreview/ClientApp/src/App.test.js delete mode 100644 dxSampleReactReportingPrintWithoutPreview/ClientApp/src/custom.css delete mode 100644 dxSampleReactReportingPrintWithoutPreview/ClientApp/src/index.js delete mode 100644 dxSampleReactReportingPrintWithoutPreview/Controllers/ReportingControllers.cs delete mode 100644 dxSampleReactReportingPrintWithoutPreview/Pages/Error.cshtml delete mode 100644 dxSampleReactReportingPrintWithoutPreview/Pages/_ViewImports.cshtml delete mode 100644 dxSampleReactReportingPrintWithoutPreview/Program.cs delete mode 100644 dxSampleReactReportingPrintWithoutPreview/Startup.cs delete mode 100644 dxSampleReactReportingPrintWithoutPreview/appsettings.Development.json delete mode 100644 dxSampleReactReportingPrintWithoutPreview/appsettings.json delete mode 100644 dxSampleReactReportingPrintWithoutPreview/dxSampleReactReportingPrintWithoutPreview.csproj create mode 100644 react-app/.gitignore create mode 100644 react-app/README.md create mode 100644 react-app/eslint.config.js create mode 100644 react-app/index.html create mode 100644 react-app/package.json create mode 100644 react-app/public/vite.svg create mode 100644 react-app/src/App.css rename dxSampleReactReportingPrintWithoutPreview/ClientApp/src/App.js => react-app/src/App.jsx (64%) create mode 100644 react-app/src/assets/react.svg rename {dxSampleReactReportingPrintWithoutPreview/ClientApp => react-app}/src/components/HomeComponent.jsx (98%) create mode 100644 react-app/src/index.css create mode 100644 react-app/src/main.jsx rename {dxSampleReactReportingPrintWithoutPreview/ClientApp => react-app}/src/registerServiceWorker.js (97%) create mode 100644 react-app/vite.config.js diff --git a/Reporting-React-Print-Without-Preview.sln b/Reporting-React-Print-Without-Preview.sln deleted file mode 100644 index 798d51f..0000000 --- a/Reporting-React-Print-Without-Preview.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29519.87 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dxSampleReactReportingPrintWithoutPreview", "dxSampleReactReportingPrintWithoutPreview\dxSampleReactReportingPrintWithoutPreview.csproj", "{C2EE00BA-5B9C-4024-A881-D32ED4EF5E46}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C2EE00BA-5B9C-4024-A881-D32ED4EF5E46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C2EE00BA-5B9C-4024-A881-D32ED4EF5E46}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C2EE00BA-5B9C-4024-A881-D32ED4EF5E46}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C2EE00BA-5B9C-4024-A881-D32ED4EF5E46}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {C6EDD4E7-97C0-42C5-9FE3-4ECF6526A975} - EndGlobalSection -EndGlobal diff --git a/dxSampleReactReportingPrintWithoutPreview/Controllers/HomeController.cs b/ServerApp/Controllers/HomeController.cs similarity index 92% rename from dxSampleReactReportingPrintWithoutPreview/Controllers/HomeController.cs rename to ServerApp/Controllers/HomeController.cs index 69018cd..69d05b5 100644 --- a/dxSampleReactReportingPrintWithoutPreview/Controllers/HomeController.cs +++ b/ServerApp/Controllers/HomeController.cs @@ -1,92 +1,92 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using DevExpress.XtraPrinting; -using DevExpress.XtraReports.UI; -using dxSampleReactReportingPrintWithoutPreview.Model; -using dxSampleReactReportingPrintWithoutPreview.PredefinedReports; -using Microsoft.AspNetCore.Mvc; - -namespace dxSampleReactReportingPrintWithoutPreview.Controllers -{ - [Route("api/[controller]")] - public class HomeController : Controller - { - public IActionResult Index() - { - return View(); - } - - public IActionResult Error() - { - ViewData["RequestId"] = Activity.Current?.Id ?? HttpContext.TraceIdentifier; - return View(); - } - [HttpGet("[action]")] - public async Task Print() - { - var report = new TestReport(); - using (var ms = new MemoryStream()) - { - await report.ExportToPdfAsync(ms, new DevExpress.XtraPrinting.PdfExportOptions { ShowPrintDialogOnOpen = true }); - return File(ms.ToArray(), System.Net.Mime.MediaTypeNames.Application.Pdf); - } - } - [HttpGet("[action]")] - public ActionResult Export(string format = "pdf") - { - format = format.ToLower(); - XtraReport report = new TestReport(); - string contentType = string.Format("application/{0}", format); - using (MemoryStream ms = new MemoryStream()) - { - switch (format) - { - case "pdf": - contentType = "application/pdf"; - report.ExportToPdf(ms); - break; - case "docx": - contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; - report.ExportToDocx(ms); - break; - case "xls": - contentType = "application/vnd.ms-excel"; - report.ExportToXls(ms); - break; - case "xlsx": - contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; - report.ExportToXlsx(ms); - break; - case "rtf": - report.ExportToRtf(ms); - break; - case "mht": - contentType = "message/rfc822"; - report.ExportToMht(ms); - break; - case "html": - contentType = "text/html"; - report.ExportToHtml(ms); - break; - case "txt": - contentType = "text/plain"; - report.ExportToText(ms); - break; - case "csv": - contentType = "text/plain"; - report.ExportToCsv(ms); - break; - case "png": - contentType = "image/png"; - report.ExportToImage(ms, new ImageExportOptions() { Format = System.Drawing.Imaging.ImageFormat.Png }); - break; - } - return File(ms.ToArray(), contentType); - } - } - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using DevExpress.XtraPrinting; +using DevExpress.XtraReports.UI; +using ServerApp.Model; +using ServerApp.PredefinedReports; +using Microsoft.AspNetCore.Mvc; + +namespace ServerApp.Controllers +{ + [Route("api/[controller]")] + public class HomeController : Controller + { + public IActionResult Index() + { + return View(); + } + + public IActionResult Error() + { + ViewData["RequestId"] = Activity.Current?.Id ?? HttpContext.TraceIdentifier; + return View(); + } + [HttpGet("[action]")] + public async Task Print() + { + var report = new TestReport(); + using (var ms = new MemoryStream()) + { + await report.ExportToPdfAsync(ms, new DevExpress.XtraPrinting.PdfExportOptions { ShowPrintDialogOnOpen = true }); + return File(ms.ToArray(), System.Net.Mime.MediaTypeNames.Application.Pdf); + } + } + [HttpGet("[action]")] + public ActionResult Export(string format = "pdf") + { + format = format.ToLower(); + XtraReport report = new TestReport(); + string contentType = string.Format("application/{0}", format); + using (MemoryStream ms = new MemoryStream()) + { + switch (format) + { + case "pdf": + contentType = "application/pdf"; + report.ExportToPdf(ms); + break; + case "docx": + contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; + report.ExportToDocx(ms); + break; + case "xls": + contentType = "application/vnd.ms-excel"; + report.ExportToXls(ms); + break; + case "xlsx": + contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + report.ExportToXlsx(ms); + break; + case "rtf": + report.ExportToRtf(ms); + break; + case "mht": + contentType = "message/rfc822"; + report.ExportToMht(ms); + break; + case "html": + contentType = "text/html"; + report.ExportToHtml(ms); + break; + case "txt": + contentType = "text/plain"; + report.ExportToText(ms); + break; + case "csv": + contentType = "text/plain"; + report.ExportToCsv(ms); + break; + case "png": + contentType = "image/png"; + report.ExportToImage(ms, new ImageExportOptions() { Format = System.Drawing.Imaging.ImageFormat.Png }); + break; + } + return File(ms.ToArray(), contentType); + } + } + } +} diff --git a/ServerApp/Controllers/ReportingControllers.cs b/ServerApp/Controllers/ReportingControllers.cs new file mode 100644 index 0000000..820bf3b --- /dev/null +++ b/ServerApp/Controllers/ReportingControllers.cs @@ -0,0 +1,45 @@ +using DevExpress.DataAccess.Sql; +using System.Collections.Generic; +using DevExpress.AspNetCore.Reporting.QueryBuilder; +using DevExpress.AspNetCore.Reporting.ReportDesigner; +using DevExpress.AspNetCore.Reporting.ReportDesigner.Native.Services; +using DevExpress.AspNetCore.Reporting.QueryBuilder.Native.Services; +using DevExpress.XtraReports.Web.ReportDesigner; +using DevExpress.XtraReports.Web.ReportDesigner.Services; +using DevExpress.AspNetCore.Reporting.WebDocumentViewer; +using DevExpress.AspNetCore.Reporting.WebDocumentViewer.Native.Services; +using Microsoft.AspNetCore.Mvc; + +namespace ServerApp.Controllers { + public class CustomWebDocumentViewerController : WebDocumentViewerController { + public CustomWebDocumentViewerController(IWebDocumentViewerMvcControllerService controllerService) : base(controllerService) { + } + } + public class CustomReportDesignerController : ReportDesignerController { + public CustomReportDesignerController(IReportDesignerMvcControllerService controllerService) : base(controllerService) { + } + + [HttpPost("[action]")] + public IActionResult GetDesignerModel([FromForm]string reportUrl, [FromServices] IReportDesignerModelBuilder designerModelBuilder, [FromForm] ReportDesignerSettingsBase designerModelSettings) { + var ds = new SqlDataSource("NWindConnectionString"); + + // Create a SQL query to access the Products data table. + SelectQuery query = SelectQueryFluentBuilder.AddTable("Products").SelectAllColumnsFromTable().Build("Products"); + ds.Queries.Add(query); + ds.RebuildResultSchema(); + + var designerModel = designerModelBuilder.Report(reportUrl) + .DataSources(dataSources => { + dataSources.Add("Northwind", ds); + }) + .BuildModel(); + designerModel.Assign(designerModelSettings); + return DesignerModel(designerModel); + } + } + + public class CustomQueryBuilderController : QueryBuilderController { + public CustomQueryBuilderController(IQueryBuilderMvcControllerService controllerService) : base(controllerService) { + } + } +} diff --git a/ServerApp/Data/ReportDbContext.cs b/ServerApp/Data/ReportDbContext.cs new file mode 100644 index 0000000..21ce175 --- /dev/null +++ b/ServerApp/Data/ReportDbContext.cs @@ -0,0 +1,63 @@ +using System.Linq; +using Microsoft.EntityFrameworkCore; + +namespace ServerApp.Data { + public class SqlDataConnectionDescription : DataConnection { } + public class JsonDataConnectionDescription : DataConnection { } + public abstract class DataConnection { + public int Id { get; set; } + public string Name { get; set; } + public string DisplayName { get; set; } + public string ConnectionString { get; set; } + } + + public class ReportItem { + public int Id { get; set; } + public string Name { get; set; } + public string DisplayName { get; set; } + public byte[] LayoutData { get; set; } + } + + public class ReportDbContext : DbContext { + public DbSet JsonDataConnections { get; set; } + public DbSet SqlDataConnections { get; set; } + public DbSet Reports { get; set; } + public ReportDbContext(DbContextOptions options) : base(options) { + } + public void InitializeDatabase() { + Database.EnsureCreated(); + + var nwindJsonDataConnectionName = "NWindProductsJson"; + if(!JsonDataConnections.Any(x => x.Name == nwindJsonDataConnectionName)) { + var newData = new JsonDataConnectionDescription { + Name = nwindJsonDataConnectionName, + DisplayName = "Northwind Products (JSON)", + ConnectionString = "Uri=Data/nwind.json" + }; + JsonDataConnections.Add(newData); + } + + + var nwindSqlDataConnectionName = "NWindConnectionString"; + if(!SqlDataConnections.Any(x => x.Name == nwindSqlDataConnectionName)) { + var newData = new SqlDataConnectionDescription { + Name = nwindSqlDataConnectionName, + DisplayName = "Northwind Data Connection", + ConnectionString = "XpoProvider=SQLite;Data Source=|DataDirectory|Data/nwind.db" + }; + SqlDataConnections.Add(newData); + } + + var reportsDataConnectionName = "ReportsDataSqlite"; + if(!SqlDataConnections.Any(x => x.Name == reportsDataConnectionName)) { + var newData = new SqlDataConnectionDescription { + Name = reportsDataConnectionName, + DisplayName = "Reports Data (Demo)", + ConnectionString = "XpoProvider=SQLite;Data Source=|DataDirectory|Data/reportsData.db" + }; + SqlDataConnections.Add(newData); + } + SaveChanges(); + } + } +} \ No newline at end of file diff --git a/dxSampleReactReportingPrintWithoutPreview/Data/nwind.db b/ServerApp/Data/nwind.db similarity index 100% rename from dxSampleReactReportingPrintWithoutPreview/Data/nwind.db rename to ServerApp/Data/nwind.db diff --git a/dxSampleReactReportingPrintWithoutPreview/Data/nwind.json b/ServerApp/Data/nwind.json similarity index 96% rename from dxSampleReactReportingPrintWithoutPreview/Data/nwind.json rename to ServerApp/Data/nwind.json index 1c6fea5..49aa018 100644 --- a/dxSampleReactReportingPrintWithoutPreview/Data/nwind.json +++ b/ServerApp/Data/nwind.json @@ -1,1005 +1,1005 @@ -{ - "Products": [ - { - "ProductID": 1, - "ProductName": "Chai", - "SupplierID": 1, - "CategoryID": 1, - "QuantityPerUnit": "10 boxes x 20 bags", - "UnitPrice": 18, - "UnitsInStock": 39, - "UnitsOnOrder": 0, - "ReorderLevel": 10, - "Discontinued": 0, - "EAN13": "070684900001" - }, - { - "ProductID": 2, - "ProductName": "Chang", - "SupplierID": 1, - "CategoryID": 1, - "QuantityPerUnit": "24 - 12 oz bottles", - "UnitPrice": 19, - "UnitsInStock": 17, - "UnitsOnOrder": 40, - "ReorderLevel": 25, - "Discontinued": 0, - "EAN13": "070684900002" - }, - { - "ProductID": 3, - "ProductName": "Aniseed Syrup", - "SupplierID": 1, - "CategoryID": 2, - "QuantityPerUnit": "12 - 550 ml bottles", - "UnitPrice": 10, - "UnitsInStock": 13, - "UnitsOnOrder": 70, - "ReorderLevel": 25, - "Discontinued": 0, - "EAN13": "070684900003" - }, - { - "ProductID": 4, - "ProductName": "Chef Anton's Cajun Seasoning", - "SupplierID": 2, - "CategoryID": 2, - "QuantityPerUnit": "48 - 6 oz jars", - "UnitPrice": 22, - "UnitsInStock": 53, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 0, - "EAN13": "070684900004" - }, - { - "ProductID": 5, - "ProductName": "Chef Anton's Gumbo Mix", - "SupplierID": 2, - "CategoryID": 2, - "QuantityPerUnit": "36 boxes", - "UnitPrice": 21.35, - "UnitsInStock": 0, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 1, - "EAN13": "070684900005" - }, - { - "ProductID": 6, - "ProductName": "Grandma's Boysenberry Spread", - "SupplierID": 3, - "CategoryID": 2, - "QuantityPerUnit": "12 - 8 oz jars", - "UnitPrice": 25, - "UnitsInStock": 120, - "UnitsOnOrder": 0, - "ReorderLevel": 25, - "Discontinued": 0, - "EAN13": "070684900006" - }, - { - "ProductID": 7, - "ProductName": "Uncle Bob's Organic Dried Pears", - "SupplierID": 3, - "CategoryID": 7, - "QuantityPerUnit": "12 - 1 lb pkgs.", - "UnitPrice": 30, - "UnitsInStock": 15, - "UnitsOnOrder": 0, - "ReorderLevel": 10, - "Discontinued": 0, - "EAN13": "070684900007" - }, - { - "ProductID": 8, - "ProductName": "Northwoods Cranberry Sauce", - "SupplierID": 3, - "CategoryID": 2, - "QuantityPerUnit": "12 - 12 oz jars", - "UnitPrice": 40, - "UnitsInStock": 6, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 0, - "EAN13": "070684900008" - }, - { - "ProductID": 9, - "ProductName": "Mishi Kobe Niku", - "SupplierID": 4, - "CategoryID": 6, - "QuantityPerUnit": "18 - 500 g pkgs.", - "UnitPrice": 97, - "UnitsInStock": 29, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 1, - "EAN13": "070684900009" - }, - { - "ProductID": 10, - "ProductName": "Ikura", - "SupplierID": 4, - "CategoryID": 8, - "QuantityPerUnit": "12 - 200 ml jars", - "UnitPrice": 31, - "UnitsInStock": 31, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 0, - "EAN13": "070684900010" - }, - { - "ProductID": 11, - "ProductName": "Queso Cabrales", - "SupplierID": 5, - "CategoryID": 4, - "QuantityPerUnit": "1 kg pkg.", - "UnitPrice": 21, - "UnitsInStock": 22, - "UnitsOnOrder": 30, - "ReorderLevel": 30, - "Discontinued": 0, - "EAN13": "070684900011" - }, - { - "ProductID": 12, - "ProductName": "Queso Manchego La Pastora", - "SupplierID": 5, - "CategoryID": 4, - "QuantityPerUnit": "10 - 500 g pkgs.", - "UnitPrice": 38, - "UnitsInStock": 86, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 0, - "EAN13": "070684900012" - }, - { - "ProductID": 13, - "ProductName": "Konbu", - "SupplierID": 6, - "CategoryID": 8, - "QuantityPerUnit": "2 kg box", - "UnitPrice": 6, - "UnitsInStock": 24, - "UnitsOnOrder": 0, - "ReorderLevel": 5, - "Discontinued": 0, - "EAN13": "070684900013" - }, - { - "ProductID": 14, - "ProductName": "Tofu", - "SupplierID": 6, - "CategoryID": 7, - "QuantityPerUnit": "40 - 100 g pkgs.", - "UnitPrice": 23.25, - "UnitsInStock": 35, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 0, - "EAN13": "070684900014" - }, - { - "ProductID": 15, - "ProductName": "Genen Shouyu", - "SupplierID": 6, - "CategoryID": 2, - "QuantityPerUnit": "24 - 250 ml bottles", - "UnitPrice": 15.5, - "UnitsInStock": 39, - "UnitsOnOrder": 0, - "ReorderLevel": 5, - "Discontinued": 0, - "EAN13": "070684900015" - }, - { - "ProductID": 16, - "ProductName": "Pavlova", - "SupplierID": 7, - "CategoryID": 3, - "QuantityPerUnit": "32 - 500 g boxes", - "UnitPrice": 17.45, - "UnitsInStock": 29, - "UnitsOnOrder": 0, - "ReorderLevel": 10, - "Discontinued": 0, - "EAN13": "070684900016" - }, - { - "ProductID": 17, - "ProductName": "Alice Mutton", - "SupplierID": 7, - "CategoryID": 6, - "QuantityPerUnit": "20 - 1 kg tins", - "UnitPrice": 39, - "UnitsInStock": 0, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 1, - "EAN13": "070684900017" - }, - { - "ProductID": 18, - "ProductName": "Carnarvon Tigers", - "SupplierID": 7, - "CategoryID": 8, - "QuantityPerUnit": "16 kg pkg.", - "UnitPrice": 62.5, - "UnitsInStock": 42, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 0, - "EAN13": "070684900018" - }, - { - "ProductID": 19, - "ProductName": "Teatime Chocolate Biscuits", - "SupplierID": 8, - "CategoryID": 3, - "QuantityPerUnit": "10 boxes x 12 pieces", - "UnitPrice": 9.2, - "UnitsInStock": 25, - "UnitsOnOrder": 0, - "ReorderLevel": 5, - "Discontinued": 0, - "EAN13": "070684900019" - }, - { - "ProductID": 20, - "ProductName": "Sir Rodney's Marmalade", - "SupplierID": 8, - "CategoryID": 3, - "QuantityPerUnit": "30 gift boxes", - "UnitPrice": 81, - "UnitsInStock": 40, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 0, - "EAN13": "070684900020" - }, - { - "ProductID": 21, - "ProductName": "Sir Rodney's Scones", - "SupplierID": 8, - "CategoryID": 3, - "QuantityPerUnit": "24 pkgs. x 4 pieces", - "UnitPrice": 10, - "UnitsInStock": 3, - "UnitsOnOrder": 40, - "ReorderLevel": 5, - "Discontinued": 0, - "EAN13": "070684900021" - }, - { - "ProductID": 22, - "ProductName": "Gustaf's Knäckebröd", - "SupplierID": 9, - "CategoryID": 5, - "QuantityPerUnit": "24 - 500 g pkgs.", - "UnitPrice": 21, - "UnitsInStock": 104, - "UnitsOnOrder": 0, - "ReorderLevel": 25, - "Discontinued": 0, - "EAN13": "070684900022" - }, - { - "ProductID": 23, - "ProductName": "Tunnbröd", - "SupplierID": 9, - "CategoryID": 5, - "QuantityPerUnit": "12 - 250 g pkgs.", - "UnitPrice": 9, - "UnitsInStock": 61, - "UnitsOnOrder": 0, - "ReorderLevel": 25, - "Discontinued": 0, - "EAN13": "070684900023" - }, - { - "ProductID": 24, - "ProductName": "Guaraná Fantástica", - "SupplierID": 10, - "CategoryID": 1, - "QuantityPerUnit": "12 - 355 ml cans", - "UnitPrice": 4.5, - "UnitsInStock": 20, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 1, - "EAN13": "070684900024" - }, - { - "ProductID": 25, - "ProductName": "NuNuCa Nuß-Nougat-Creme", - "SupplierID": 11, - "CategoryID": 3, - "QuantityPerUnit": "20 - 450 g glasses", - "UnitPrice": 14, - "UnitsInStock": 76, - "UnitsOnOrder": 0, - "ReorderLevel": 30, - "Discontinued": 0, - "EAN13": "070684900025" - }, - { - "ProductID": 26, - "ProductName": "Gumbär Gummibärchen", - "SupplierID": 11, - "CategoryID": 3, - "QuantityPerUnit": "100 - 250 g bags", - "UnitPrice": 31.23, - "UnitsInStock": 15, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 0, - "EAN13": "070684900026" - }, - { - "ProductID": 27, - "ProductName": "Schoggi Schokolade", - "SupplierID": 11, - "CategoryID": 3, - "QuantityPerUnit": "100 - 100 g pieces", - "UnitPrice": 43.9, - "UnitsInStock": 49, - "UnitsOnOrder": 0, - "ReorderLevel": 30, - "Discontinued": 0, - "EAN13": "070684900027" - }, - { - "ProductID": 28, - "ProductName": "Rössle Sauerkraut", - "SupplierID": 12, - "CategoryID": 7, - "QuantityPerUnit": "25 - 825 g cans", - "UnitPrice": 45.6, - "UnitsInStock": 26, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 1, - "EAN13": "070684900028" - }, - { - "ProductID": 29, - "ProductName": "Thüringer Rostbratwurst", - "SupplierID": 12, - "CategoryID": 6, - "QuantityPerUnit": "50 bags x 30 sausgs.", - "UnitPrice": 123.79, - "UnitsInStock": 0, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 1, - "EAN13": "070684900029" - }, - { - "ProductID": 30, - "ProductName": "Nord-Ost Matjeshering", - "SupplierID": 13, - "CategoryID": 8, - "QuantityPerUnit": "10 - 200 g glasses", - "UnitPrice": 25.89, - "UnitsInStock": 10, - "UnitsOnOrder": 0, - "ReorderLevel": 15, - "Discontinued": 0, - "EAN13": "070684900030" - }, - { - "ProductID": 31, - "ProductName": "Gorgonzola Telino", - "SupplierID": 14, - "CategoryID": 4, - "QuantityPerUnit": "12 - 100 g pkgs", - "UnitPrice": 12.5, - "UnitsInStock": 0, - "UnitsOnOrder": 70, - "ReorderLevel": 20, - "Discontinued": 0, - "EAN13": "070684900031" - }, - { - "ProductID": 32, - "ProductName": "Mascarpone Fabioli", - "SupplierID": 14, - "CategoryID": 4, - "QuantityPerUnit": "24 - 200 g pkgs.", - "UnitPrice": 32, - "UnitsInStock": 9, - "UnitsOnOrder": 40, - "ReorderLevel": 25, - "Discontinued": 0, - "EAN13": "070684900032" - }, - { - "ProductID": 33, - "ProductName": "Geitost", - "SupplierID": 15, - "CategoryID": 4, - "QuantityPerUnit": "500 g", - "UnitPrice": 2.5, - "UnitsInStock": 112, - "UnitsOnOrder": 0, - "ReorderLevel": 20, - "Discontinued": 0, - "EAN13": "070684900033" - }, - { - "ProductID": 34, - "ProductName": "Sasquatch Ale", - "SupplierID": 16, - "CategoryID": 1, - "QuantityPerUnit": "24 - 12 oz bottles", - "UnitPrice": 14, - "UnitsInStock": 111, - "UnitsOnOrder": 0, - "ReorderLevel": 15, - "Discontinued": 0, - "EAN13": "070684900034" - }, - { - "ProductID": 35, - "ProductName": "Steeleye Stout", - "SupplierID": 16, - "CategoryID": 1, - "QuantityPerUnit": "24 - 12 oz bottles", - "UnitPrice": 18, - "UnitsInStock": 20, - "UnitsOnOrder": 0, - "ReorderLevel": 15, - "Discontinued": 0, - "EAN13": "070684900035" - }, - { - "ProductID": 36, - "ProductName": "Inlagd Sill", - "SupplierID": 17, - "CategoryID": 8, - "QuantityPerUnit": "24 - 250 g jars", - "UnitPrice": 19, - "UnitsInStock": 112, - "UnitsOnOrder": 0, - "ReorderLevel": 20, - "Discontinued": 0, - "EAN13": "070684900036" - }, - { - "ProductID": 37, - "ProductName": "Gravad lax", - "SupplierID": 17, - "CategoryID": 8, - "QuantityPerUnit": "12 - 500 g pkgs.", - "UnitPrice": 26, - "UnitsInStock": 11, - "UnitsOnOrder": 50, - "ReorderLevel": 25, - "Discontinued": 0, - "EAN13": "070684900037" - }, - { - "ProductID": 38, - "ProductName": "Côte de Blaye", - "SupplierID": 18, - "CategoryID": 1, - "QuantityPerUnit": "12 - 75 cl bottles", - "UnitPrice": 263.5, - "UnitsInStock": 17, - "UnitsOnOrder": 0, - "ReorderLevel": 15, - "Discontinued": 0, - "EAN13": "070684900038" - }, - { - "ProductID": 39, - "ProductName": "Chartreuse verte", - "SupplierID": 18, - "CategoryID": 1, - "QuantityPerUnit": "750 cc per bottle", - "UnitPrice": 18, - "UnitsInStock": 69, - "UnitsOnOrder": 0, - "ReorderLevel": 5, - "Discontinued": 0, - "EAN13": "070684900039" - }, - { - "ProductID": 40, - "ProductName": "Boston Crab Meat", - "SupplierID": 19, - "CategoryID": 8, - "QuantityPerUnit": "24 - 4 oz tins", - "UnitPrice": 18.4, - "UnitsInStock": 123, - "UnitsOnOrder": 0, - "ReorderLevel": 30, - "Discontinued": 0, - "EAN13": "070684900040" - }, - { - "ProductID": 41, - "ProductName": "Jack's New England Clam Chowder", - "SupplierID": 19, - "CategoryID": 8, - "QuantityPerUnit": "12 - 12 oz cans", - "UnitPrice": 9.65, - "UnitsInStock": 85, - "UnitsOnOrder": 0, - "ReorderLevel": 10, - "Discontinued": 0, - "EAN13": "070684900041" - }, - { - "ProductID": 42, - "ProductName": "Singaporean Hokkien Fried Mee", - "SupplierID": 20, - "CategoryID": 5, - "QuantityPerUnit": "32 - 1 kg pkgs.", - "UnitPrice": 14, - "UnitsInStock": 26, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 1, - "EAN13": "070684900042" - }, - { - "ProductID": 43, - "ProductName": "Ipoh Coffee", - "SupplierID": 20, - "CategoryID": 1, - "QuantityPerUnit": "16 - 500 g tins", - "UnitPrice": 46, - "UnitsInStock": 17, - "UnitsOnOrder": 10, - "ReorderLevel": 25, - "Discontinued": 0, - "EAN13": "070684900043" - }, - { - "ProductID": 44, - "ProductName": "Gula Malacca", - "SupplierID": 20, - "CategoryID": 2, - "QuantityPerUnit": "20 - 2 kg bags", - "UnitPrice": 19.45, - "UnitsInStock": 27, - "UnitsOnOrder": 0, - "ReorderLevel": 15, - "Discontinued": 0, - "EAN13": "070684900044" - }, - { - "ProductID": 45, - "ProductName": "Rogede sild", - "SupplierID": 21, - "CategoryID": 8, - "QuantityPerUnit": "1k pkg.", - "UnitPrice": 9.5, - "UnitsInStock": 5, - "UnitsOnOrder": 70, - "ReorderLevel": 15, - "Discontinued": 0, - "EAN13": "070684900045" - }, - { - "ProductID": 46, - "ProductName": "Spegesild", - "SupplierID": 21, - "CategoryID": 8, - "QuantityPerUnit": "4 - 450 g glasses", - "UnitPrice": 12, - "UnitsInStock": 95, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 0, - "EAN13": "070684900046" - }, - { - "ProductID": 47, - "ProductName": "Zaanse koeken", - "SupplierID": 22, - "CategoryID": 3, - "QuantityPerUnit": "10 - 4 oz boxes", - "UnitPrice": 9.5, - "UnitsInStock": 36, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 0, - "EAN13": "070684900047" - }, - { - "ProductID": 48, - "ProductName": "Chocolade", - "SupplierID": 22, - "CategoryID": 3, - "QuantityPerUnit": "10 pkgs.", - "UnitPrice": 12.75, - "UnitsInStock": 15, - "UnitsOnOrder": 70, - "ReorderLevel": 25, - "Discontinued": 0, - "EAN13": "070684900048" - }, - { - "ProductID": 49, - "ProductName": "Maxilaku", - "SupplierID": 23, - "CategoryID": 3, - "QuantityPerUnit": "24 - 50 g pkgs.", - "UnitPrice": 20, - "UnitsInStock": 10, - "UnitsOnOrder": 60, - "ReorderLevel": 15, - "Discontinued": 0, - "EAN13": "070684900049" - }, - { - "ProductID": 50, - "ProductName": "Valkoinen suklaa", - "SupplierID": 23, - "CategoryID": 3, - "QuantityPerUnit": "12 - 100 g bars", - "UnitPrice": 16.25, - "UnitsInStock": 65, - "UnitsOnOrder": 0, - "ReorderLevel": 30, - "Discontinued": 0, - "EAN13": "070684900050" - }, - { - "ProductID": 51, - "ProductName": "Manjimup Dried Apples", - "SupplierID": 24, - "CategoryID": 7, - "QuantityPerUnit": "50 - 300 g pkgs.", - "UnitPrice": 53, - "UnitsInStock": 20, - "UnitsOnOrder": 0, - "ReorderLevel": 10, - "Discontinued": 0, - "EAN13": "070684900051" - }, - { - "ProductID": 52, - "ProductName": "Filo Mix", - "SupplierID": 24, - "CategoryID": 5, - "QuantityPerUnit": "16 - 2 kg boxes", - "UnitPrice": 7, - "UnitsInStock": 38, - "UnitsOnOrder": 0, - "ReorderLevel": 25, - "Discontinued": 0, - "EAN13": "070684900052" - }, - { - "ProductID": 53, - "ProductName": "Perth Pasties", - "SupplierID": 24, - "CategoryID": 6, - "QuantityPerUnit": "48 pieces", - "UnitPrice": 32.8, - "UnitsInStock": 0, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 1, - "EAN13": "070684900053" - }, - { - "ProductID": 54, - "ProductName": "Tourtière", - "SupplierID": 25, - "CategoryID": 6, - "QuantityPerUnit": "16 pies", - "UnitPrice": 7.45, - "UnitsInStock": 21, - "UnitsOnOrder": 0, - "ReorderLevel": 10, - "Discontinued": 0, - "EAN13": "070684900054" - }, - { - "ProductID": 55, - "ProductName": "Pâté chinois", - "SupplierID": 25, - "CategoryID": 6, - "QuantityPerUnit": "24 boxes x 2 pies", - "UnitPrice": 24, - "UnitsInStock": 115, - "UnitsOnOrder": 0, - "ReorderLevel": 20, - "Discontinued": 0, - "EAN13": "070684900055" - }, - { - "ProductID": 56, - "ProductName": "Gnocchi di nonna Alice", - "SupplierID": 26, - "CategoryID": 5, - "QuantityPerUnit": "24 - 250 g pkgs.", - "UnitPrice": 38, - "UnitsInStock": 21, - "UnitsOnOrder": 10, - "ReorderLevel": 30, - "Discontinued": 0, - "EAN13": "070684900056" - }, - { - "ProductID": 57, - "ProductName": "Ravioli Angelo", - "SupplierID": 26, - "CategoryID": 5, - "QuantityPerUnit": "24 - 250 g pkgs.", - "UnitPrice": 19.5, - "UnitsInStock": 36, - "UnitsOnOrder": 0, - "ReorderLevel": 20, - "Discontinued": 0, - "EAN13": "070684900057" - }, - { - "ProductID": 58, - "ProductName": "Escargots de Bourgogne", - "SupplierID": 27, - "CategoryID": 8, - "QuantityPerUnit": "24 pieces", - "UnitPrice": 13.25, - "UnitsInStock": 62, - "UnitsOnOrder": 0, - "ReorderLevel": 20, - "Discontinued": 0, - "EAN13": "070684900058" - }, - { - "ProductID": 59, - "ProductName": "Raclette Courdavault", - "SupplierID": 28, - "CategoryID": 4, - "QuantityPerUnit": "5 kg pkg.", - "UnitPrice": 55, - "UnitsInStock": 79, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 0, - "EAN13": "070684900059" - }, - { - "ProductID": 60, - "ProductName": "Camembert Pierrot", - "SupplierID": 28, - "CategoryID": 4, - "QuantityPerUnit": "15 - 300 g rounds", - "UnitPrice": 34, - "UnitsInStock": 19, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 0, - "EAN13": "070684900060" - }, - { - "ProductID": 61, - "ProductName": "Sirop d'érable", - "SupplierID": 29, - "CategoryID": 2, - "QuantityPerUnit": "24 - 500 ml bottles", - "UnitPrice": 28.5, - "UnitsInStock": 113, - "UnitsOnOrder": 0, - "ReorderLevel": 25, - "Discontinued": 0, - "EAN13": "070684900061" - }, - { - "ProductID": 62, - "ProductName": "Tarte au sucre", - "SupplierID": 29, - "CategoryID": 3, - "QuantityPerUnit": "48 pies", - "UnitPrice": 49.3, - "UnitsInStock": 17, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 0, - "EAN13": "070684900062" - }, - { - "ProductID": 63, - "ProductName": "Vegie-spread", - "SupplierID": 7, - "CategoryID": 2, - "QuantityPerUnit": "15 - 625 g jars", - "UnitPrice": 43.9, - "UnitsInStock": 24, - "UnitsOnOrder": 0, - "ReorderLevel": 5, - "Discontinued": 0, - "EAN13": "070684900063" - }, - { - "ProductID": 64, - "ProductName": "Wimmers gute Semmelknödel", - "SupplierID": 12, - "CategoryID": 5, - "QuantityPerUnit": "20 bags x 4 pieces", - "UnitPrice": 33.25, - "UnitsInStock": 22, - "UnitsOnOrder": 80, - "ReorderLevel": 30, - "Discontinued": 0, - "EAN13": "070684900064" - }, - { - "ProductID": 65, - "ProductName": "Louisiana Fiery Hot Pepper Sauce", - "SupplierID": 2, - "CategoryID": 2, - "QuantityPerUnit": "32 - 8 oz bottles", - "UnitPrice": 21.05, - "UnitsInStock": 76, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 0, - "EAN13": "070684900065" - }, - { - "ProductID": 66, - "ProductName": "Louisiana Hot Spiced Okra", - "SupplierID": 2, - "CategoryID": 2, - "QuantityPerUnit": "24 - 8 oz jars", - "UnitPrice": 17, - "UnitsInStock": 4, - "UnitsOnOrder": 100, - "ReorderLevel": 20, - "Discontinued": 0, - "EAN13": "070684900066" - }, - { - "ProductID": 67, - "ProductName": "Laughing Lumberjack Lager", - "SupplierID": 16, - "CategoryID": 1, - "QuantityPerUnit": "24 - 12 oz bottles", - "UnitPrice": 14, - "UnitsInStock": 52, - "UnitsOnOrder": 0, - "ReorderLevel": 10, - "Discontinued": 0, - "EAN13": "070684900067" - }, - { - "ProductID": 68, - "ProductName": "Scottish Longbreads", - "SupplierID": 8, - "CategoryID": 3, - "QuantityPerUnit": "10 boxes x 8 pieces", - "UnitPrice": 12.5, - "UnitsInStock": 6, - "UnitsOnOrder": 10, - "ReorderLevel": 15, - "Discontinued": 0, - "EAN13": "070684900068" - }, - { - "ProductID": 69, - "ProductName": "Gudbrandsdalsost", - "SupplierID": 15, - "CategoryID": 4, - "QuantityPerUnit": "10 kg pkg.", - "UnitPrice": 36, - "UnitsInStock": 26, - "UnitsOnOrder": 0, - "ReorderLevel": 15, - "Discontinued": 0, - "EAN13": "070684900069" - }, - { - "ProductID": 70, - "ProductName": "Outback Lager", - "SupplierID": 7, - "CategoryID": 1, - "QuantityPerUnit": "24 - 355 ml bottles", - "UnitPrice": 15, - "UnitsInStock": 15, - "UnitsOnOrder": 10, - "ReorderLevel": 30, - "Discontinued": 0, - "EAN13": "070684900070" - }, - { - "ProductID": 71, - "ProductName": "Flotemysost", - "SupplierID": 15, - "CategoryID": 4, - "QuantityPerUnit": "10 - 500 g pkgs.", - "UnitPrice": 21.5, - "UnitsInStock": 26, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 0, - "EAN13": "070684900071" - }, - { - "ProductID": 72, - "ProductName": "Mozzarella di Giovanni", - "SupplierID": 14, - "CategoryID": 4, - "QuantityPerUnit": "24 - 200 g pkgs.", - "UnitPrice": 34.8, - "UnitsInStock": 14, - "UnitsOnOrder": 0, - "ReorderLevel": 0, - "Discontinued": 0, - "EAN13": "070684900072" - }, - { - "ProductID": 73, - "ProductName": "Röd Kaviar", - "SupplierID": 17, - "CategoryID": 8, - "QuantityPerUnit": "24 - 150 g jars", - "UnitPrice": 15, - "UnitsInStock": 101, - "UnitsOnOrder": 0, - "ReorderLevel": 5, - "Discontinued": 0, - "EAN13": "070684900073" - }, - { - "ProductID": 74, - "ProductName": "Longlife Tofu", - "SupplierID": 4, - "CategoryID": 7, - "QuantityPerUnit": "5 kg pkg.", - "UnitPrice": 10, - "UnitsInStock": 4, - "UnitsOnOrder": 20, - "ReorderLevel": 5, - "Discontinued": 0, - "EAN13": "070684900074" - }, - { - "ProductID": 75, - "ProductName": "Rhönbräu Klosterbier", - "SupplierID": 12, - "CategoryID": 1, - "QuantityPerUnit": "24 - 0.5 l bottles", - "UnitPrice": 7.75, - "UnitsInStock": 125, - "UnitsOnOrder": 0, - "ReorderLevel": 25, - "Discontinued": 0, - "EAN13": "070684900075" - }, - { - "ProductID": 76, - "ProductName": "Lakkalikööri", - "SupplierID": 23, - "CategoryID": 1, - "QuantityPerUnit": "500 ml", - "UnitPrice": 18, - "UnitsInStock": 57, - "UnitsOnOrder": 0, - "ReorderLevel": 20, - "Discontinued": 0, - "EAN13": "070684900076" - }, - { - "ProductID": 77, - "ProductName": "Original Frankfurter grüne Soße", - "SupplierID": 12, - "CategoryID": 2, - "QuantityPerUnit": "12 boxes", - "UnitPrice": 13, - "UnitsInStock": 32, - "UnitsOnOrder": 0, - "ReorderLevel": 15, - "Discontinued": 0, - "EAN13": "070684900077" - } - ] -} +{ + "Products": [ + { + "ProductID": 1, + "ProductName": "Chai", + "SupplierID": 1, + "CategoryID": 1, + "QuantityPerUnit": "10 boxes x 20 bags", + "UnitPrice": 18, + "UnitsInStock": 39, + "UnitsOnOrder": 0, + "ReorderLevel": 10, + "Discontinued": 0, + "EAN13": "070684900001" + }, + { + "ProductID": 2, + "ProductName": "Chang", + "SupplierID": 1, + "CategoryID": 1, + "QuantityPerUnit": "24 - 12 oz bottles", + "UnitPrice": 19, + "UnitsInStock": 17, + "UnitsOnOrder": 40, + "ReorderLevel": 25, + "Discontinued": 0, + "EAN13": "070684900002" + }, + { + "ProductID": 3, + "ProductName": "Aniseed Syrup", + "SupplierID": 1, + "CategoryID": 2, + "QuantityPerUnit": "12 - 550 ml bottles", + "UnitPrice": 10, + "UnitsInStock": 13, + "UnitsOnOrder": 70, + "ReorderLevel": 25, + "Discontinued": 0, + "EAN13": "070684900003" + }, + { + "ProductID": 4, + "ProductName": "Chef Anton's Cajun Seasoning", + "SupplierID": 2, + "CategoryID": 2, + "QuantityPerUnit": "48 - 6 oz jars", + "UnitPrice": 22, + "UnitsInStock": 53, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 0, + "EAN13": "070684900004" + }, + { + "ProductID": 5, + "ProductName": "Chef Anton's Gumbo Mix", + "SupplierID": 2, + "CategoryID": 2, + "QuantityPerUnit": "36 boxes", + "UnitPrice": 21.35, + "UnitsInStock": 0, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 1, + "EAN13": "070684900005" + }, + { + "ProductID": 6, + "ProductName": "Grandma's Boysenberry Spread", + "SupplierID": 3, + "CategoryID": 2, + "QuantityPerUnit": "12 - 8 oz jars", + "UnitPrice": 25, + "UnitsInStock": 120, + "UnitsOnOrder": 0, + "ReorderLevel": 25, + "Discontinued": 0, + "EAN13": "070684900006" + }, + { + "ProductID": 7, + "ProductName": "Uncle Bob's Organic Dried Pears", + "SupplierID": 3, + "CategoryID": 7, + "QuantityPerUnit": "12 - 1 lb pkgs.", + "UnitPrice": 30, + "UnitsInStock": 15, + "UnitsOnOrder": 0, + "ReorderLevel": 10, + "Discontinued": 0, + "EAN13": "070684900007" + }, + { + "ProductID": 8, + "ProductName": "Northwoods Cranberry Sauce", + "SupplierID": 3, + "CategoryID": 2, + "QuantityPerUnit": "12 - 12 oz jars", + "UnitPrice": 40, + "UnitsInStock": 6, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 0, + "EAN13": "070684900008" + }, + { + "ProductID": 9, + "ProductName": "Mishi Kobe Niku", + "SupplierID": 4, + "CategoryID": 6, + "QuantityPerUnit": "18 - 500 g pkgs.", + "UnitPrice": 97, + "UnitsInStock": 29, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 1, + "EAN13": "070684900009" + }, + { + "ProductID": 10, + "ProductName": "Ikura", + "SupplierID": 4, + "CategoryID": 8, + "QuantityPerUnit": "12 - 200 ml jars", + "UnitPrice": 31, + "UnitsInStock": 31, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 0, + "EAN13": "070684900010" + }, + { + "ProductID": 11, + "ProductName": "Queso Cabrales", + "SupplierID": 5, + "CategoryID": 4, + "QuantityPerUnit": "1 kg pkg.", + "UnitPrice": 21, + "UnitsInStock": 22, + "UnitsOnOrder": 30, + "ReorderLevel": 30, + "Discontinued": 0, + "EAN13": "070684900011" + }, + { + "ProductID": 12, + "ProductName": "Queso Manchego La Pastora", + "SupplierID": 5, + "CategoryID": 4, + "QuantityPerUnit": "10 - 500 g pkgs.", + "UnitPrice": 38, + "UnitsInStock": 86, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 0, + "EAN13": "070684900012" + }, + { + "ProductID": 13, + "ProductName": "Konbu", + "SupplierID": 6, + "CategoryID": 8, + "QuantityPerUnit": "2 kg box", + "UnitPrice": 6, + "UnitsInStock": 24, + "UnitsOnOrder": 0, + "ReorderLevel": 5, + "Discontinued": 0, + "EAN13": "070684900013" + }, + { + "ProductID": 14, + "ProductName": "Tofu", + "SupplierID": 6, + "CategoryID": 7, + "QuantityPerUnit": "40 - 100 g pkgs.", + "UnitPrice": 23.25, + "UnitsInStock": 35, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 0, + "EAN13": "070684900014" + }, + { + "ProductID": 15, + "ProductName": "Genen Shouyu", + "SupplierID": 6, + "CategoryID": 2, + "QuantityPerUnit": "24 - 250 ml bottles", + "UnitPrice": 15.5, + "UnitsInStock": 39, + "UnitsOnOrder": 0, + "ReorderLevel": 5, + "Discontinued": 0, + "EAN13": "070684900015" + }, + { + "ProductID": 16, + "ProductName": "Pavlova", + "SupplierID": 7, + "CategoryID": 3, + "QuantityPerUnit": "32 - 500 g boxes", + "UnitPrice": 17.45, + "UnitsInStock": 29, + "UnitsOnOrder": 0, + "ReorderLevel": 10, + "Discontinued": 0, + "EAN13": "070684900016" + }, + { + "ProductID": 17, + "ProductName": "Alice Mutton", + "SupplierID": 7, + "CategoryID": 6, + "QuantityPerUnit": "20 - 1 kg tins", + "UnitPrice": 39, + "UnitsInStock": 0, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 1, + "EAN13": "070684900017" + }, + { + "ProductID": 18, + "ProductName": "Carnarvon Tigers", + "SupplierID": 7, + "CategoryID": 8, + "QuantityPerUnit": "16 kg pkg.", + "UnitPrice": 62.5, + "UnitsInStock": 42, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 0, + "EAN13": "070684900018" + }, + { + "ProductID": 19, + "ProductName": "Teatime Chocolate Biscuits", + "SupplierID": 8, + "CategoryID": 3, + "QuantityPerUnit": "10 boxes x 12 pieces", + "UnitPrice": 9.2, + "UnitsInStock": 25, + "UnitsOnOrder": 0, + "ReorderLevel": 5, + "Discontinued": 0, + "EAN13": "070684900019" + }, + { + "ProductID": 20, + "ProductName": "Sir Rodney's Marmalade", + "SupplierID": 8, + "CategoryID": 3, + "QuantityPerUnit": "30 gift boxes", + "UnitPrice": 81, + "UnitsInStock": 40, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 0, + "EAN13": "070684900020" + }, + { + "ProductID": 21, + "ProductName": "Sir Rodney's Scones", + "SupplierID": 8, + "CategoryID": 3, + "QuantityPerUnit": "24 pkgs. x 4 pieces", + "UnitPrice": 10, + "UnitsInStock": 3, + "UnitsOnOrder": 40, + "ReorderLevel": 5, + "Discontinued": 0, + "EAN13": "070684900021" + }, + { + "ProductID": 22, + "ProductName": "Gustaf's Knäckebröd", + "SupplierID": 9, + "CategoryID": 5, + "QuantityPerUnit": "24 - 500 g pkgs.", + "UnitPrice": 21, + "UnitsInStock": 104, + "UnitsOnOrder": 0, + "ReorderLevel": 25, + "Discontinued": 0, + "EAN13": "070684900022" + }, + { + "ProductID": 23, + "ProductName": "Tunnbröd", + "SupplierID": 9, + "CategoryID": 5, + "QuantityPerUnit": "12 - 250 g pkgs.", + "UnitPrice": 9, + "UnitsInStock": 61, + "UnitsOnOrder": 0, + "ReorderLevel": 25, + "Discontinued": 0, + "EAN13": "070684900023" + }, + { + "ProductID": 24, + "ProductName": "Guaraná Fantástica", + "SupplierID": 10, + "CategoryID": 1, + "QuantityPerUnit": "12 - 355 ml cans", + "UnitPrice": 4.5, + "UnitsInStock": 20, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 1, + "EAN13": "070684900024" + }, + { + "ProductID": 25, + "ProductName": "NuNuCa Nuß-Nougat-Creme", + "SupplierID": 11, + "CategoryID": 3, + "QuantityPerUnit": "20 - 450 g glasses", + "UnitPrice": 14, + "UnitsInStock": 76, + "UnitsOnOrder": 0, + "ReorderLevel": 30, + "Discontinued": 0, + "EAN13": "070684900025" + }, + { + "ProductID": 26, + "ProductName": "Gumbär Gummibärchen", + "SupplierID": 11, + "CategoryID": 3, + "QuantityPerUnit": "100 - 250 g bags", + "UnitPrice": 31.23, + "UnitsInStock": 15, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 0, + "EAN13": "070684900026" + }, + { + "ProductID": 27, + "ProductName": "Schoggi Schokolade", + "SupplierID": 11, + "CategoryID": 3, + "QuantityPerUnit": "100 - 100 g pieces", + "UnitPrice": 43.9, + "UnitsInStock": 49, + "UnitsOnOrder": 0, + "ReorderLevel": 30, + "Discontinued": 0, + "EAN13": "070684900027" + }, + { + "ProductID": 28, + "ProductName": "Rössle Sauerkraut", + "SupplierID": 12, + "CategoryID": 7, + "QuantityPerUnit": "25 - 825 g cans", + "UnitPrice": 45.6, + "UnitsInStock": 26, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 1, + "EAN13": "070684900028" + }, + { + "ProductID": 29, + "ProductName": "Thüringer Rostbratwurst", + "SupplierID": 12, + "CategoryID": 6, + "QuantityPerUnit": "50 bags x 30 sausgs.", + "UnitPrice": 123.79, + "UnitsInStock": 0, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 1, + "EAN13": "070684900029" + }, + { + "ProductID": 30, + "ProductName": "Nord-Ost Matjeshering", + "SupplierID": 13, + "CategoryID": 8, + "QuantityPerUnit": "10 - 200 g glasses", + "UnitPrice": 25.89, + "UnitsInStock": 10, + "UnitsOnOrder": 0, + "ReorderLevel": 15, + "Discontinued": 0, + "EAN13": "070684900030" + }, + { + "ProductID": 31, + "ProductName": "Gorgonzola Telino", + "SupplierID": 14, + "CategoryID": 4, + "QuantityPerUnit": "12 - 100 g pkgs", + "UnitPrice": 12.5, + "UnitsInStock": 0, + "UnitsOnOrder": 70, + "ReorderLevel": 20, + "Discontinued": 0, + "EAN13": "070684900031" + }, + { + "ProductID": 32, + "ProductName": "Mascarpone Fabioli", + "SupplierID": 14, + "CategoryID": 4, + "QuantityPerUnit": "24 - 200 g pkgs.", + "UnitPrice": 32, + "UnitsInStock": 9, + "UnitsOnOrder": 40, + "ReorderLevel": 25, + "Discontinued": 0, + "EAN13": "070684900032" + }, + { + "ProductID": 33, + "ProductName": "Geitost", + "SupplierID": 15, + "CategoryID": 4, + "QuantityPerUnit": "500 g", + "UnitPrice": 2.5, + "UnitsInStock": 112, + "UnitsOnOrder": 0, + "ReorderLevel": 20, + "Discontinued": 0, + "EAN13": "070684900033" + }, + { + "ProductID": 34, + "ProductName": "Sasquatch Ale", + "SupplierID": 16, + "CategoryID": 1, + "QuantityPerUnit": "24 - 12 oz bottles", + "UnitPrice": 14, + "UnitsInStock": 111, + "UnitsOnOrder": 0, + "ReorderLevel": 15, + "Discontinued": 0, + "EAN13": "070684900034" + }, + { + "ProductID": 35, + "ProductName": "Steeleye Stout", + "SupplierID": 16, + "CategoryID": 1, + "QuantityPerUnit": "24 - 12 oz bottles", + "UnitPrice": 18, + "UnitsInStock": 20, + "UnitsOnOrder": 0, + "ReorderLevel": 15, + "Discontinued": 0, + "EAN13": "070684900035" + }, + { + "ProductID": 36, + "ProductName": "Inlagd Sill", + "SupplierID": 17, + "CategoryID": 8, + "QuantityPerUnit": "24 - 250 g jars", + "UnitPrice": 19, + "UnitsInStock": 112, + "UnitsOnOrder": 0, + "ReorderLevel": 20, + "Discontinued": 0, + "EAN13": "070684900036" + }, + { + "ProductID": 37, + "ProductName": "Gravad lax", + "SupplierID": 17, + "CategoryID": 8, + "QuantityPerUnit": "12 - 500 g pkgs.", + "UnitPrice": 26, + "UnitsInStock": 11, + "UnitsOnOrder": 50, + "ReorderLevel": 25, + "Discontinued": 0, + "EAN13": "070684900037" + }, + { + "ProductID": 38, + "ProductName": "Côte de Blaye", + "SupplierID": 18, + "CategoryID": 1, + "QuantityPerUnit": "12 - 75 cl bottles", + "UnitPrice": 263.5, + "UnitsInStock": 17, + "UnitsOnOrder": 0, + "ReorderLevel": 15, + "Discontinued": 0, + "EAN13": "070684900038" + }, + { + "ProductID": 39, + "ProductName": "Chartreuse verte", + "SupplierID": 18, + "CategoryID": 1, + "QuantityPerUnit": "750 cc per bottle", + "UnitPrice": 18, + "UnitsInStock": 69, + "UnitsOnOrder": 0, + "ReorderLevel": 5, + "Discontinued": 0, + "EAN13": "070684900039" + }, + { + "ProductID": 40, + "ProductName": "Boston Crab Meat", + "SupplierID": 19, + "CategoryID": 8, + "QuantityPerUnit": "24 - 4 oz tins", + "UnitPrice": 18.4, + "UnitsInStock": 123, + "UnitsOnOrder": 0, + "ReorderLevel": 30, + "Discontinued": 0, + "EAN13": "070684900040" + }, + { + "ProductID": 41, + "ProductName": "Jack's New England Clam Chowder", + "SupplierID": 19, + "CategoryID": 8, + "QuantityPerUnit": "12 - 12 oz cans", + "UnitPrice": 9.65, + "UnitsInStock": 85, + "UnitsOnOrder": 0, + "ReorderLevel": 10, + "Discontinued": 0, + "EAN13": "070684900041" + }, + { + "ProductID": 42, + "ProductName": "Singaporean Hokkien Fried Mee", + "SupplierID": 20, + "CategoryID": 5, + "QuantityPerUnit": "32 - 1 kg pkgs.", + "UnitPrice": 14, + "UnitsInStock": 26, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 1, + "EAN13": "070684900042" + }, + { + "ProductID": 43, + "ProductName": "Ipoh Coffee", + "SupplierID": 20, + "CategoryID": 1, + "QuantityPerUnit": "16 - 500 g tins", + "UnitPrice": 46, + "UnitsInStock": 17, + "UnitsOnOrder": 10, + "ReorderLevel": 25, + "Discontinued": 0, + "EAN13": "070684900043" + }, + { + "ProductID": 44, + "ProductName": "Gula Malacca", + "SupplierID": 20, + "CategoryID": 2, + "QuantityPerUnit": "20 - 2 kg bags", + "UnitPrice": 19.45, + "UnitsInStock": 27, + "UnitsOnOrder": 0, + "ReorderLevel": 15, + "Discontinued": 0, + "EAN13": "070684900044" + }, + { + "ProductID": 45, + "ProductName": "Rogede sild", + "SupplierID": 21, + "CategoryID": 8, + "QuantityPerUnit": "1k pkg.", + "UnitPrice": 9.5, + "UnitsInStock": 5, + "UnitsOnOrder": 70, + "ReorderLevel": 15, + "Discontinued": 0, + "EAN13": "070684900045" + }, + { + "ProductID": 46, + "ProductName": "Spegesild", + "SupplierID": 21, + "CategoryID": 8, + "QuantityPerUnit": "4 - 450 g glasses", + "UnitPrice": 12, + "UnitsInStock": 95, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 0, + "EAN13": "070684900046" + }, + { + "ProductID": 47, + "ProductName": "Zaanse koeken", + "SupplierID": 22, + "CategoryID": 3, + "QuantityPerUnit": "10 - 4 oz boxes", + "UnitPrice": 9.5, + "UnitsInStock": 36, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 0, + "EAN13": "070684900047" + }, + { + "ProductID": 48, + "ProductName": "Chocolade", + "SupplierID": 22, + "CategoryID": 3, + "QuantityPerUnit": "10 pkgs.", + "UnitPrice": 12.75, + "UnitsInStock": 15, + "UnitsOnOrder": 70, + "ReorderLevel": 25, + "Discontinued": 0, + "EAN13": "070684900048" + }, + { + "ProductID": 49, + "ProductName": "Maxilaku", + "SupplierID": 23, + "CategoryID": 3, + "QuantityPerUnit": "24 - 50 g pkgs.", + "UnitPrice": 20, + "UnitsInStock": 10, + "UnitsOnOrder": 60, + "ReorderLevel": 15, + "Discontinued": 0, + "EAN13": "070684900049" + }, + { + "ProductID": 50, + "ProductName": "Valkoinen suklaa", + "SupplierID": 23, + "CategoryID": 3, + "QuantityPerUnit": "12 - 100 g bars", + "UnitPrice": 16.25, + "UnitsInStock": 65, + "UnitsOnOrder": 0, + "ReorderLevel": 30, + "Discontinued": 0, + "EAN13": "070684900050" + }, + { + "ProductID": 51, + "ProductName": "Manjimup Dried Apples", + "SupplierID": 24, + "CategoryID": 7, + "QuantityPerUnit": "50 - 300 g pkgs.", + "UnitPrice": 53, + "UnitsInStock": 20, + "UnitsOnOrder": 0, + "ReorderLevel": 10, + "Discontinued": 0, + "EAN13": "070684900051" + }, + { + "ProductID": 52, + "ProductName": "Filo Mix", + "SupplierID": 24, + "CategoryID": 5, + "QuantityPerUnit": "16 - 2 kg boxes", + "UnitPrice": 7, + "UnitsInStock": 38, + "UnitsOnOrder": 0, + "ReorderLevel": 25, + "Discontinued": 0, + "EAN13": "070684900052" + }, + { + "ProductID": 53, + "ProductName": "Perth Pasties", + "SupplierID": 24, + "CategoryID": 6, + "QuantityPerUnit": "48 pieces", + "UnitPrice": 32.8, + "UnitsInStock": 0, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 1, + "EAN13": "070684900053" + }, + { + "ProductID": 54, + "ProductName": "Tourtière", + "SupplierID": 25, + "CategoryID": 6, + "QuantityPerUnit": "16 pies", + "UnitPrice": 7.45, + "UnitsInStock": 21, + "UnitsOnOrder": 0, + "ReorderLevel": 10, + "Discontinued": 0, + "EAN13": "070684900054" + }, + { + "ProductID": 55, + "ProductName": "Pâté chinois", + "SupplierID": 25, + "CategoryID": 6, + "QuantityPerUnit": "24 boxes x 2 pies", + "UnitPrice": 24, + "UnitsInStock": 115, + "UnitsOnOrder": 0, + "ReorderLevel": 20, + "Discontinued": 0, + "EAN13": "070684900055" + }, + { + "ProductID": 56, + "ProductName": "Gnocchi di nonna Alice", + "SupplierID": 26, + "CategoryID": 5, + "QuantityPerUnit": "24 - 250 g pkgs.", + "UnitPrice": 38, + "UnitsInStock": 21, + "UnitsOnOrder": 10, + "ReorderLevel": 30, + "Discontinued": 0, + "EAN13": "070684900056" + }, + { + "ProductID": 57, + "ProductName": "Ravioli Angelo", + "SupplierID": 26, + "CategoryID": 5, + "QuantityPerUnit": "24 - 250 g pkgs.", + "UnitPrice": 19.5, + "UnitsInStock": 36, + "UnitsOnOrder": 0, + "ReorderLevel": 20, + "Discontinued": 0, + "EAN13": "070684900057" + }, + { + "ProductID": 58, + "ProductName": "Escargots de Bourgogne", + "SupplierID": 27, + "CategoryID": 8, + "QuantityPerUnit": "24 pieces", + "UnitPrice": 13.25, + "UnitsInStock": 62, + "UnitsOnOrder": 0, + "ReorderLevel": 20, + "Discontinued": 0, + "EAN13": "070684900058" + }, + { + "ProductID": 59, + "ProductName": "Raclette Courdavault", + "SupplierID": 28, + "CategoryID": 4, + "QuantityPerUnit": "5 kg pkg.", + "UnitPrice": 55, + "UnitsInStock": 79, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 0, + "EAN13": "070684900059" + }, + { + "ProductID": 60, + "ProductName": "Camembert Pierrot", + "SupplierID": 28, + "CategoryID": 4, + "QuantityPerUnit": "15 - 300 g rounds", + "UnitPrice": 34, + "UnitsInStock": 19, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 0, + "EAN13": "070684900060" + }, + { + "ProductID": 61, + "ProductName": "Sirop d'érable", + "SupplierID": 29, + "CategoryID": 2, + "QuantityPerUnit": "24 - 500 ml bottles", + "UnitPrice": 28.5, + "UnitsInStock": 113, + "UnitsOnOrder": 0, + "ReorderLevel": 25, + "Discontinued": 0, + "EAN13": "070684900061" + }, + { + "ProductID": 62, + "ProductName": "Tarte au sucre", + "SupplierID": 29, + "CategoryID": 3, + "QuantityPerUnit": "48 pies", + "UnitPrice": 49.3, + "UnitsInStock": 17, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 0, + "EAN13": "070684900062" + }, + { + "ProductID": 63, + "ProductName": "Vegie-spread", + "SupplierID": 7, + "CategoryID": 2, + "QuantityPerUnit": "15 - 625 g jars", + "UnitPrice": 43.9, + "UnitsInStock": 24, + "UnitsOnOrder": 0, + "ReorderLevel": 5, + "Discontinued": 0, + "EAN13": "070684900063" + }, + { + "ProductID": 64, + "ProductName": "Wimmers gute Semmelknödel", + "SupplierID": 12, + "CategoryID": 5, + "QuantityPerUnit": "20 bags x 4 pieces", + "UnitPrice": 33.25, + "UnitsInStock": 22, + "UnitsOnOrder": 80, + "ReorderLevel": 30, + "Discontinued": 0, + "EAN13": "070684900064" + }, + { + "ProductID": 65, + "ProductName": "Louisiana Fiery Hot Pepper Sauce", + "SupplierID": 2, + "CategoryID": 2, + "QuantityPerUnit": "32 - 8 oz bottles", + "UnitPrice": 21.05, + "UnitsInStock": 76, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 0, + "EAN13": "070684900065" + }, + { + "ProductID": 66, + "ProductName": "Louisiana Hot Spiced Okra", + "SupplierID": 2, + "CategoryID": 2, + "QuantityPerUnit": "24 - 8 oz jars", + "UnitPrice": 17, + "UnitsInStock": 4, + "UnitsOnOrder": 100, + "ReorderLevel": 20, + "Discontinued": 0, + "EAN13": "070684900066" + }, + { + "ProductID": 67, + "ProductName": "Laughing Lumberjack Lager", + "SupplierID": 16, + "CategoryID": 1, + "QuantityPerUnit": "24 - 12 oz bottles", + "UnitPrice": 14, + "UnitsInStock": 52, + "UnitsOnOrder": 0, + "ReorderLevel": 10, + "Discontinued": 0, + "EAN13": "070684900067" + }, + { + "ProductID": 68, + "ProductName": "Scottish Longbreads", + "SupplierID": 8, + "CategoryID": 3, + "QuantityPerUnit": "10 boxes x 8 pieces", + "UnitPrice": 12.5, + "UnitsInStock": 6, + "UnitsOnOrder": 10, + "ReorderLevel": 15, + "Discontinued": 0, + "EAN13": "070684900068" + }, + { + "ProductID": 69, + "ProductName": "Gudbrandsdalsost", + "SupplierID": 15, + "CategoryID": 4, + "QuantityPerUnit": "10 kg pkg.", + "UnitPrice": 36, + "UnitsInStock": 26, + "UnitsOnOrder": 0, + "ReorderLevel": 15, + "Discontinued": 0, + "EAN13": "070684900069" + }, + { + "ProductID": 70, + "ProductName": "Outback Lager", + "SupplierID": 7, + "CategoryID": 1, + "QuantityPerUnit": "24 - 355 ml bottles", + "UnitPrice": 15, + "UnitsInStock": 15, + "UnitsOnOrder": 10, + "ReorderLevel": 30, + "Discontinued": 0, + "EAN13": "070684900070" + }, + { + "ProductID": 71, + "ProductName": "Flotemysost", + "SupplierID": 15, + "CategoryID": 4, + "QuantityPerUnit": "10 - 500 g pkgs.", + "UnitPrice": 21.5, + "UnitsInStock": 26, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 0, + "EAN13": "070684900071" + }, + { + "ProductID": 72, + "ProductName": "Mozzarella di Giovanni", + "SupplierID": 14, + "CategoryID": 4, + "QuantityPerUnit": "24 - 200 g pkgs.", + "UnitPrice": 34.8, + "UnitsInStock": 14, + "UnitsOnOrder": 0, + "ReorderLevel": 0, + "Discontinued": 0, + "EAN13": "070684900072" + }, + { + "ProductID": 73, + "ProductName": "Röd Kaviar", + "SupplierID": 17, + "CategoryID": 8, + "QuantityPerUnit": "24 - 150 g jars", + "UnitPrice": 15, + "UnitsInStock": 101, + "UnitsOnOrder": 0, + "ReorderLevel": 5, + "Discontinued": 0, + "EAN13": "070684900073" + }, + { + "ProductID": 74, + "ProductName": "Longlife Tofu", + "SupplierID": 4, + "CategoryID": 7, + "QuantityPerUnit": "5 kg pkg.", + "UnitPrice": 10, + "UnitsInStock": 4, + "UnitsOnOrder": 20, + "ReorderLevel": 5, + "Discontinued": 0, + "EAN13": "070684900074" + }, + { + "ProductID": 75, + "ProductName": "Rhönbräu Klosterbier", + "SupplierID": 12, + "CategoryID": 1, + "QuantityPerUnit": "24 - 0.5 l bottles", + "UnitPrice": 7.75, + "UnitsInStock": 125, + "UnitsOnOrder": 0, + "ReorderLevel": 25, + "Discontinued": 0, + "EAN13": "070684900075" + }, + { + "ProductID": 76, + "ProductName": "Lakkalikööri", + "SupplierID": 23, + "CategoryID": 1, + "QuantityPerUnit": "500 ml", + "UnitPrice": 18, + "UnitsInStock": 57, + "UnitsOnOrder": 0, + "ReorderLevel": 20, + "Discontinued": 0, + "EAN13": "070684900076" + }, + { + "ProductID": 77, + "ProductName": "Original Frankfurter grüne Soße", + "SupplierID": 12, + "CategoryID": 2, + "QuantityPerUnit": "12 boxes", + "UnitPrice": 13, + "UnitsInStock": 32, + "UnitsOnOrder": 0, + "ReorderLevel": 15, + "Discontinued": 0, + "EAN13": "070684900077" + } + ] +} diff --git a/ServerApp/Data/reportsData.db b/ServerApp/Data/reportsData.db new file mode 100644 index 0000000000000000000000000000000000000000..7a11360f74a80745fc6a2494ea7ace96101474ff GIT binary patch literal 32768 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 V{Rg)GclVsw`u}1JbiMWe{{Uu`6DError. +

An error occurred while processing your request.

+ +@if (Model.ShowRequestId) +{ +

+ Request ID: @Model.RequestId +

+} + +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. +

diff --git a/dxSampleReactReportingPrintWithoutPreview/Pages/Error.cshtml.cs b/ServerApp/Pages/Error.cshtml.cs similarity index 67% rename from dxSampleReactReportingPrintWithoutPreview/Pages/Error.cshtml.cs rename to ServerApp/Pages/Error.cshtml.cs index 269fff4..edc520f 100644 --- a/dxSampleReactReportingPrintWithoutPreview/Pages/Error.cshtml.cs +++ b/ServerApp/Pages/Error.cshtml.cs @@ -1,31 +1,23 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace dxSampleReactReportingPrintWithoutPreview.Pages -{ - [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] - public class ErrorModel : PageModel - { - private readonly ILogger _logger; - - public ErrorModel(ILogger logger) - { - _logger = logger; - } - - public string RequestId { get; set; } - - public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - - public void OnGet() - { - RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; - } - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace ServerApp.Pages +{ + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + public class ErrorModel : PageModel + { + public string RequestId { get; set; } + + public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + + public void OnGet() + { + RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; + } + } +} diff --git a/ServerApp/Pages/_ViewImports.cshtml b/ServerApp/Pages/_ViewImports.cshtml new file mode 100644 index 0000000..099b708 --- /dev/null +++ b/ServerApp/Pages/_ViewImports.cshtml @@ -0,0 +1,3 @@ +@using ServerApp +@namespace ServerApp.Pages +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/dxSampleReactReportingPrintWithoutPreview/PredefinedReports/ReportsFactory.cs b/ServerApp/PredefinedReports/ReportsFactory.cs similarity index 81% rename from dxSampleReactReportingPrintWithoutPreview/PredefinedReports/ReportsFactory.cs rename to ServerApp/PredefinedReports/ReportsFactory.cs index dd7ec5c..d111bc5 100644 --- a/dxSampleReactReportingPrintWithoutPreview/PredefinedReports/ReportsFactory.cs +++ b/ServerApp/PredefinedReports/ReportsFactory.cs @@ -1,16 +1,16 @@ -using DevExpress.XtraReports.UI; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace dxSampleReactReportingPrintWithoutPreview.PredefinedReports -{ - public static class ReportsFactory - { - public static Dictionary> Reports = new Dictionary>() - { - ["TestReport"] = () => new TestReport() - }; - } -} +using DevExpress.XtraReports.UI; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ServerApp.PredefinedReports +{ + public static class ReportsFactory + { + public static Dictionary> Reports = new Dictionary>() + { + ["TestReport"] = () => new TestReport() + }; + } +} diff --git a/dxSampleReactReportingPrintWithoutPreview/PredefinedReports/TestReport.Designer.cs b/ServerApp/PredefinedReports/TestReport.Designer.cs similarity index 94% rename from dxSampleReactReportingPrintWithoutPreview/PredefinedReports/TestReport.Designer.cs rename to ServerApp/PredefinedReports/TestReport.Designer.cs index 2ad33d4..b3c7f1a 100644 --- a/dxSampleReactReportingPrintWithoutPreview/PredefinedReports/TestReport.Designer.cs +++ b/ServerApp/PredefinedReports/TestReport.Designer.cs @@ -1,74 +1,74 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace dxSampleReactReportingPrintWithoutPreview.PredefinedReports { - - public partial class TestReport : DevExpress.XtraReports.UI.XtraReport { - private void InitializeComponent() { - DevExpress.XtraReports.ReportInitializer reportInitializer = new DevExpress.XtraReports.ReportInitializer(this, "dxSampleReactReportingPrintWithoutPreview.PredefinedReports.TestReport.repx"); - - // Controls - this.TopMargin = reportInitializer.GetControl("TopMargin"); - this.ReportHeader = reportInitializer.GetControl("ReportHeader"); - this.GroupHeader1 = reportInitializer.GetControl("GroupHeader1"); - this.Detail = reportInitializer.GetControl("Detail"); - this.BottomMargin = reportInitializer.GetControl("BottomMargin"); - this.label1 = reportInitializer.GetControl("label1"); - this.table1 = reportInitializer.GetControl("table1"); - this.tableRow1 = reportInitializer.GetControl("tableRow1"); - this.tableCell1 = reportInitializer.GetControl("tableCell1"); - this.tableCell2 = reportInitializer.GetControl("tableCell2"); - this.tableCell3 = reportInitializer.GetControl("tableCell3"); - this.table2 = reportInitializer.GetControl("table2"); - this.tableRow2 = reportInitializer.GetControl("tableRow2"); - this.tableCell4 = reportInitializer.GetControl("tableCell4"); - this.tableCell5 = reportInitializer.GetControl("tableCell5"); - this.tableCell6 = reportInitializer.GetControl("tableCell6"); - this.pictureBox1 = reportInitializer.GetControl("pictureBox1"); - this.pageInfo1 = reportInitializer.GetControl("pageInfo1"); - this.pageInfo2 = reportInitializer.GetControl("pageInfo2"); - - // Data Sources - this.sqlDataSource1 = reportInitializer.GetDataSource("sqlDataSource1"); - - // Styles - this.Title = reportInitializer.GetStyle("Title"); - this.DetailCaption1 = reportInitializer.GetStyle("DetailCaption1"); - this.DetailData1 = reportInitializer.GetStyle("DetailData1"); - this.DetailData3_Odd = reportInitializer.GetStyle("DetailData3_Odd"); - this.PageInfo = reportInitializer.GetStyle("PageInfo"); - } - private DevExpress.XtraReports.UI.TopMarginBand TopMargin; - private DevExpress.XtraReports.UI.ReportHeaderBand ReportHeader; - private DevExpress.XtraReports.UI.GroupHeaderBand GroupHeader1; - private DevExpress.XtraReports.UI.DetailBand Detail; - private DevExpress.XtraReports.UI.BottomMarginBand BottomMargin; - private DevExpress.XtraReports.UI.XRLabel label1; - private DevExpress.XtraReports.UI.XRTable table1; - private DevExpress.XtraReports.UI.XRTableRow tableRow1; - private DevExpress.XtraReports.UI.XRTableCell tableCell1; - private DevExpress.XtraReports.UI.XRTableCell tableCell2; - private DevExpress.XtraReports.UI.XRTableCell tableCell3; - private DevExpress.XtraReports.UI.XRTable table2; - private DevExpress.XtraReports.UI.XRTableRow tableRow2; - private DevExpress.XtraReports.UI.XRTableCell tableCell4; - private DevExpress.XtraReports.UI.XRTableCell tableCell5; - private DevExpress.XtraReports.UI.XRTableCell tableCell6; - private DevExpress.XtraReports.UI.XRPictureBox pictureBox1; - private DevExpress.XtraReports.UI.XRPageInfo pageInfo1; - private DevExpress.XtraReports.UI.XRPageInfo pageInfo2; - private DevExpress.DataAccess.Sql.SqlDataSource sqlDataSource1; - private DevExpress.XtraReports.UI.XRControlStyle Title; - private DevExpress.XtraReports.UI.XRControlStyle DetailCaption1; - private DevExpress.XtraReports.UI.XRControlStyle DetailData1; - private DevExpress.XtraReports.UI.XRControlStyle DetailData3_Odd; - private DevExpress.XtraReports.UI.XRControlStyle PageInfo; - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ServerApp.PredefinedReports { + + public partial class TestReport : DevExpress.XtraReports.UI.XtraReport { + private void InitializeComponent() { + DevExpress.XtraReports.ReportInitializer reportInitializer = new DevExpress.XtraReports.ReportInitializer(this, "ServerApp.PredefinedReports.TestReport.repx"); + + // Controls + this.TopMargin = reportInitializer.GetControl("TopMargin"); + this.ReportHeader = reportInitializer.GetControl("ReportHeader"); + this.GroupHeader1 = reportInitializer.GetControl("GroupHeader1"); + this.Detail = reportInitializer.GetControl("Detail"); + this.BottomMargin = reportInitializer.GetControl("BottomMargin"); + this.label1 = reportInitializer.GetControl("label1"); + this.table1 = reportInitializer.GetControl("table1"); + this.tableRow1 = reportInitializer.GetControl("tableRow1"); + this.tableCell1 = reportInitializer.GetControl("tableCell1"); + this.tableCell2 = reportInitializer.GetControl("tableCell2"); + this.tableCell3 = reportInitializer.GetControl("tableCell3"); + this.table2 = reportInitializer.GetControl("table2"); + this.tableRow2 = reportInitializer.GetControl("tableRow2"); + this.tableCell4 = reportInitializer.GetControl("tableCell4"); + this.tableCell5 = reportInitializer.GetControl("tableCell5"); + this.tableCell6 = reportInitializer.GetControl("tableCell6"); + this.pictureBox1 = reportInitializer.GetControl("pictureBox1"); + this.pageInfo1 = reportInitializer.GetControl("pageInfo1"); + this.pageInfo2 = reportInitializer.GetControl("pageInfo2"); + + // Data Sources + this.sqlDataSource1 = reportInitializer.GetDataSource("sqlDataSource1"); + + // Styles + this.Title = reportInitializer.GetStyle("Title"); + this.DetailCaption1 = reportInitializer.GetStyle("DetailCaption1"); + this.DetailData1 = reportInitializer.GetStyle("DetailData1"); + this.DetailData3_Odd = reportInitializer.GetStyle("DetailData3_Odd"); + this.PageInfo = reportInitializer.GetStyle("PageInfo"); + } + private DevExpress.XtraReports.UI.TopMarginBand TopMargin; + private DevExpress.XtraReports.UI.ReportHeaderBand ReportHeader; + private DevExpress.XtraReports.UI.GroupHeaderBand GroupHeader1; + private DevExpress.XtraReports.UI.DetailBand Detail; + private DevExpress.XtraReports.UI.BottomMarginBand BottomMargin; + private DevExpress.XtraReports.UI.XRLabel label1; + private DevExpress.XtraReports.UI.XRTable table1; + private DevExpress.XtraReports.UI.XRTableRow tableRow1; + private DevExpress.XtraReports.UI.XRTableCell tableCell1; + private DevExpress.XtraReports.UI.XRTableCell tableCell2; + private DevExpress.XtraReports.UI.XRTableCell tableCell3; + private DevExpress.XtraReports.UI.XRTable table2; + private DevExpress.XtraReports.UI.XRTableRow tableRow2; + private DevExpress.XtraReports.UI.XRTableCell tableCell4; + private DevExpress.XtraReports.UI.XRTableCell tableCell5; + private DevExpress.XtraReports.UI.XRTableCell tableCell6; + private DevExpress.XtraReports.UI.XRPictureBox pictureBox1; + private DevExpress.XtraReports.UI.XRPageInfo pageInfo1; + private DevExpress.XtraReports.UI.XRPageInfo pageInfo2; + private DevExpress.DataAccess.Sql.SqlDataSource sqlDataSource1; + private DevExpress.XtraReports.UI.XRControlStyle Title; + private DevExpress.XtraReports.UI.XRControlStyle DetailCaption1; + private DevExpress.XtraReports.UI.XRControlStyle DetailData1; + private DevExpress.XtraReports.UI.XRControlStyle DetailData3_Odd; + private DevExpress.XtraReports.UI.XRControlStyle PageInfo; + } +} diff --git a/dxSampleReactReportingPrintWithoutPreview/PredefinedReports/TestReport.cs b/ServerApp/PredefinedReports/TestReport.cs similarity index 68% rename from dxSampleReactReportingPrintWithoutPreview/PredefinedReports/TestReport.cs rename to ServerApp/PredefinedReports/TestReport.cs index ea89e09..4c1b1c2 100644 --- a/dxSampleReactReportingPrintWithoutPreview/PredefinedReports/TestReport.cs +++ b/ServerApp/PredefinedReports/TestReport.cs @@ -1,13 +1,13 @@ -using System; -using DevExpress.XtraReports.UI; - -namespace dxSampleReactReportingPrintWithoutPreview.PredefinedReports -{ - public partial class TestReport - { - public TestReport() - { - InitializeComponent(); - } - } -} +using System; +using DevExpress.XtraReports.UI; + +namespace ServerApp.PredefinedReports +{ + public partial class TestReport + { + public TestReport() + { + InitializeComponent(); + } + } +} diff --git a/dxSampleReactReportingPrintWithoutPreview/PredefinedReports/TestReport.repx b/ServerApp/PredefinedReports/TestReport.repx similarity index 98% rename from dxSampleReactReportingPrintWithoutPreview/PredefinedReports/TestReport.repx rename to ServerApp/PredefinedReports/TestReport.repx index d415451..cd79a0e 100644 --- a/dxSampleReactReportingPrintWithoutPreview/PredefinedReports/TestReport.repx +++ b/ServerApp/PredefinedReports/TestReport.repx @@ -1,79 +1,79 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ServerApp/PredefinedReports/TestReport.resx b/ServerApp/PredefinedReports/TestReport.resx new file mode 100644 index 0000000..e36faff --- /dev/null +++ b/ServerApp/PredefinedReports/TestReport.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + PERhdGFTZXQgTmFtZT0ic3FsRGF0YVNvdXJjZTEiPjxWaWV3IE5hbWU9IkNhdGVnb3JpZXMiPjxGaWVsZCBOYW1lPSJDYXRlZ29yeUlEIiBUeXBlPSJJbnQ2NCIgLz48RmllbGQgTmFtZT0iQ2F0ZWdvcnlOYW1lIiBUeXBlPSJTdHJpbmciIC8+PEZpZWxkIE5hbWU9IkRlc2NyaXB0aW9uIiBUeXBlPSJTdHJpbmciIC8+PEZpZWxkIE5hbWU9IlBpY3R1cmUiIFR5cGU9IkJ5dGVBcnJheSIgLz48RmllbGQgTmFtZT0iSWNvbjE3IiBUeXBlPSJCeXRlQXJyYXkiIC8+PEZpZWxkIE5hbWU9Ikljb24yNSIgVHlwZT0iQnl0ZUFycmF5IiAvPjwvVmlldz48L0RhdGFTZXQ+ + + \ No newline at end of file diff --git a/ServerApp/Program.cs b/ServerApp/Program.cs new file mode 100644 index 0000000..3d19a94 --- /dev/null +++ b/ServerApp/Program.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using DevExpress.AspNetCore; +using DevExpress.AspNetCore.Reporting; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using DevExpress.XtraReports.Web.Extensions; +using DevExpress.Security.Resources; +using Microsoft.EntityFrameworkCore; +using ServerApp.Services; +using ServerApp.Data; + +var builder = WebApplication.CreateBuilder(args); + +AppDomain.CurrentDomain.SetData("DataDirectory", builder.Environment.ContentRootPath); +builder.Services.AddDevExpressControls(); +builder.Services.AddScoped(); +builder.Services.AddMvc(); +builder.Services.ConfigureReportingServices(configurator => { + if(builder.Environment.IsDevelopment()) + configurator.UseDevelopmentMode(); + + configurator.ConfigureReportDesigner(designerConfigurator => { + designerConfigurator.RegisterDataSourceWizardConfigFileConnectionStringsProvider(); + + }); + configurator.ConfigureWebDocumentViewer(viewerConfigurator => { + viewerConfigurator.UseCachedReportSourceBuilder(); + }); +}); +builder.Services.AddDbContext(options => options.UseSqlite(builder.Configuration.GetConnectionString("ReportsDataConnectionString"))); + +builder.Services.AddCors(options => { + options.AddPolicy("AllowCorsPolicy", builder => { + // Allow all ports on local host. + builder.SetIsOriginAllowed(origin => new Uri(origin).Host == "localhost"); + builder.AllowAnyHeader(); + builder.AllowAnyMethod(); + }); +}); +var app = builder.Build(); +using(var scope = app.Services.CreateScope()) { + var services = scope.ServiceProvider; + services.GetService().InitializeDatabase(); +} +var contentDirectoryAllowRule = DirectoryAccessRule.Allow(new DirectoryInfo(Path.Combine(app.Environment.ContentRootPath, "Content")).FullName); +AccessSettings.ReportingSpecificResources.TrySetRules(contentDirectoryAllowRule, UrlAccessRule.Allow()); +DevExpress.XtraReports.Configuration.Settings.Default.UserDesignerOptions.DataBindingMode = DevExpress.XtraReports.UI.DataBindingMode.Expressions; + +app.UseHttpsRedirection(); +app.UseStaticFiles(); +app.UseRouting(); +app.UseCors("AllowCorsPolicy"); + +app.UseDevExpressControls(); +System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls12; +app.MapControllerRoute( + name: "default", + pattern: "{controller}/{action=Index}/{id?}"); + + +app.Run(); \ No newline at end of file diff --git a/ServerApp/ServerApp.csproj b/ServerApp/ServerApp.csproj new file mode 100644 index 0000000..e9ba2d4 --- /dev/null +++ b/ServerApp/ServerApp.csproj @@ -0,0 +1,40 @@ + + + + net8.0 + enable + true + + + + + + + + + + + + + + + + TestReport.repx + + + TestReport.repx + + + + + + + + + PreserveNewest + + + PreserveNewest + + + \ No newline at end of file diff --git a/ServerApp/ServerApp.sln b/ServerApp/ServerApp.sln new file mode 100644 index 0000000..134f041 --- /dev/null +++ b/ServerApp/ServerApp.sln @@ -0,0 +1,24 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServerApp", "ServerApp.csproj", "{933F1940-34C7-1FE1-951C-31F7F1886CF1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {933F1940-34C7-1FE1-951C-31F7F1886CF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {933F1940-34C7-1FE1-951C-31F7F1886CF1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {933F1940-34C7-1FE1-951C-31F7F1886CF1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {933F1940-34C7-1FE1-951C-31F7F1886CF1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B8F6522D-274E-4800-A7FA-D4BDB02AD133} + EndGlobalSection +EndGlobal diff --git a/dxSampleReactReportingPrintWithoutPreview/Services/ReportStorageWebExtension.cs b/ServerApp/Services/ReportStorageWebExtension.cs similarity index 95% rename from dxSampleReactReportingPrintWithoutPreview/Services/ReportStorageWebExtension.cs rename to ServerApp/Services/ReportStorageWebExtension.cs index b0eb0cf..a44bb12 100644 --- a/dxSampleReactReportingPrintWithoutPreview/Services/ReportStorageWebExtension.cs +++ b/ServerApp/Services/ReportStorageWebExtension.cs @@ -1,96 +1,96 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using DevExpress.XtraReports.Web.Extensions; -using DevExpress.XtraReports.UI; -using Microsoft.AspNetCore.Hosting; -using dxSampleReactReportingPrintWithoutPreview.PredefinedReports; - -namespace dxSampleReactReportingPrintWithoutPreview.Services -{ - public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extensions.ReportStorageWebExtension - { - readonly string ReportDirectory; - const string FileExtension = ".repx"; - public CustomReportStorageWebExtension(IWebHostEnvironment env) { - ReportDirectory = Path.Combine(env.ContentRootPath, "Reports"); - if (!Directory.Exists(ReportDirectory)) { - Directory.CreateDirectory(ReportDirectory); - } - } - - private bool IsWithinReportsFolder(string url, string folder) { - var rootDirectory = new DirectoryInfo(folder); - var fileInfo = new FileInfo(Path.Combine(folder, url)); - return fileInfo.Directory.FullName.ToLower().StartsWith(rootDirectory.FullName.ToLower()); - } - - public override bool CanSetData(string url) { - // Determines whether or not it is possible to store a report by a given URL. - // For instance, make the CanSetData method return false for reports that should be read-only in your storage. - // This method is called only for valid URLs (i.e., if the IsValidUrl method returned true) before the SetData method is called. - - return true; - } - - public override bool IsValidUrl(string url) { - // Determines whether or not the URL passed to the current Report Storage is valid. - // For instance, implement your own logic to prohibit URLs that contain white spaces or some other special characters. - // This method is called before the CanSetData and GetData methods. - - return Path.GetFileName(url) == url; - } - - public override byte[] GetData(string url) { - // Returns report layout data stored in a Report Storage using the specified URL. - // This method is called only for valid URLs after the IsValidUrl method is called. - try { - if (Directory.EnumerateFiles(ReportDirectory).Select(Path.GetFileNameWithoutExtension).Contains(url)) - { - return File.ReadAllBytes(Path.Combine(ReportDirectory, url + FileExtension)); - } - if (ReportsFactory.Reports.ContainsKey(url)) - { - using (MemoryStream ms = new MemoryStream()) { - ReportsFactory.Reports[url]().SaveLayoutToXml(ms); - return ms.ToArray(); - } - } - } catch (Exception ex) { - throw new DevExpress.XtraReports.Web.ClientControls.FaultException("Could not get report data.", ex); - } - throw new DevExpress.XtraReports.Web.ClientControls.FaultException(string.Format("Could not find report '{0}'.", url)); - } - - public override Dictionary GetUrls() { - // Returns a dictionary of the existing report URLs and display names. - // This method is called when running the Report Designer, - // before the Open Report and Save Report dialogs are shown and after a new report is saved to a storage. - - return Directory.GetFiles(ReportDirectory, "*" + FileExtension) - .Select(Path.GetFileNameWithoutExtension) - .Union(ReportsFactory.Reports.Select(x => x.Key)) - .ToDictionary(x => x); - } - - public override void SetData(XtraReport report, string url) { - // Stores the specified report to a Report Storage using the specified URL. - // This method is called only after the IsValidUrl and CanSetData methods are called. - if(!IsWithinReportsFolder(url, ReportDirectory)) - throw new DevExpress.XtraReports.Web.ClientControls.FaultException("Invalid report name."); - report.SaveLayoutToXml(Path.Combine(ReportDirectory, url + FileExtension)); - } - - public override string SetNewData(XtraReport report, string defaultUrl) { - // Stores the specified report using a new URL. - // The IsValidUrl and CanSetData methods are never called before this method. - // You can validate and correct the specified URL directly in the SetNewData method implementation - // and return the resulting URL used to save a report in your storage. - SetData(report, defaultUrl); - return defaultUrl; - } - } -} +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using DevExpress.XtraReports.Web.Extensions; +using DevExpress.XtraReports.UI; +using Microsoft.AspNetCore.Hosting; +using ServerApp.PredefinedReports; + +namespace ServerApp.Services +{ + public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extensions.ReportStorageWebExtension + { + readonly string ReportDirectory; + const string FileExtension = ".repx"; + public CustomReportStorageWebExtension(IWebHostEnvironment env) { + ReportDirectory = Path.Combine(env.ContentRootPath, "Reports"); + if (!Directory.Exists(ReportDirectory)) { + Directory.CreateDirectory(ReportDirectory); + } + } + + private bool IsWithinReportsFolder(string url, string folder) { + var rootDirectory = new DirectoryInfo(folder); + var fileInfo = new FileInfo(Path.Combine(folder, url)); + return fileInfo.Directory.FullName.ToLower().StartsWith(rootDirectory.FullName.ToLower()); + } + + public override bool CanSetData(string url) { + // Determines whether or not it is possible to store a report by a given URL. + // For instance, make the CanSetData method return false for reports that should be read-only in your storage. + // This method is called only for valid URLs (i.e., if the IsValidUrl method returned true) before the SetData method is called. + + return true; + } + + public override bool IsValidUrl(string url) { + // Determines whether or not the URL passed to the current Report Storage is valid. + // For instance, implement your own logic to prohibit URLs that contain white spaces or some other special characters. + // This method is called before the CanSetData and GetData methods. + + return Path.GetFileName(url) == url; + } + + public override byte[] GetData(string url) { + // Returns report layout data stored in a Report Storage using the specified URL. + // This method is called only for valid URLs after the IsValidUrl method is called. + try { + if (Directory.EnumerateFiles(ReportDirectory).Select(Path.GetFileNameWithoutExtension).Contains(url)) + { + return File.ReadAllBytes(Path.Combine(ReportDirectory, url + FileExtension)); + } + if (ReportsFactory.Reports.ContainsKey(url)) + { + using (MemoryStream ms = new MemoryStream()) { + ReportsFactory.Reports[url]().SaveLayoutToXml(ms); + return ms.ToArray(); + } + } + } catch (Exception ex) { + throw new DevExpress.XtraReports.Web.ClientControls.FaultException("Could not get report data.", ex); + } + throw new DevExpress.XtraReports.Web.ClientControls.FaultException(string.Format("Could not find report '{0}'.", url)); + } + + public override Dictionary GetUrls() { + // Returns a dictionary of the existing report URLs and display names. + // This method is called when running the Report Designer, + // before the Open Report and Save Report dialogs are shown and after a new report is saved to a storage. + + return Directory.GetFiles(ReportDirectory, "*" + FileExtension) + .Select(Path.GetFileNameWithoutExtension) + .Union(ReportsFactory.Reports.Select(x => x.Key)) + .ToDictionary(x => x); + } + + public override void SetData(XtraReport report, string url) { + // Stores the specified report to a Report Storage using the specified URL. + // This method is called only after the IsValidUrl and CanSetData methods are called. + if(!IsWithinReportsFolder(url, ReportDirectory)) + throw new DevExpress.XtraReports.Web.ClientControls.FaultException("Invalid report name."); + report.SaveLayoutToXml(Path.Combine(ReportDirectory, url + FileExtension)); + } + + public override string SetNewData(XtraReport report, string defaultUrl) { + // Stores the specified report using a new URL. + // The IsValidUrl and CanSetData methods are never called before this method. + // You can validate and correct the specified URL directly in the SetNewData method implementation + // and return the resulting URL used to save a report in your storage. + SetData(report, defaultUrl); + return defaultUrl; + } + } +} diff --git a/ServerApp/appsettings.Development.json b/ServerApp/appsettings.Development.json new file mode 100644 index 0000000..cc7fe77 --- /dev/null +++ b/ServerApp/appsettings.Development.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "System": "Information", + "Microsoft": "Information", + "DevExpress": "Information" + } + } +} diff --git a/ServerApp/appsettings.json b/ServerApp/appsettings.json new file mode 100644 index 0000000..2f226ec --- /dev/null +++ b/ServerApp/appsettings.json @@ -0,0 +1,12 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "DevExpress": "Warning" + } + }, + "ConnectionStrings": { + "NWindConnectionString": "XpoProvider=SQLite;Data Source=|DataDirectory|Data/nwind.db", + "ReportsDataConnectionString": "Filename=Data/reportsData.db" + } +} diff --git a/dxSampleReactReportingPrintWithoutPreview/.gitignore b/dxSampleReactReportingPrintWithoutPreview/.gitignore deleted file mode 100644 index 67e842d..0000000 --- a/dxSampleReactReportingPrintWithoutPreview/.gitignore +++ /dev/null @@ -1,232 +0,0 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -build/ -bld/ -bin/ -Bin/ -obj/ -Obj/ - -# Visual Studio 2015 cache/options directory -.vs/ -/wwwroot/dist/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Microsoft Azure ApplicationInsights config file -ApplicationInsights.config - -# Windows Store app package directory -AppPackages/ -BundleArtifacts/ - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.pfx -*.publishsettings -orleans.codegen.cs - -/node_modules - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe - -# FAKE - F# Make -.fake/ diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/.gitignore b/dxSampleReactReportingPrintWithoutPreview/ClientApp/.gitignore deleted file mode 100644 index 69e588b..0000000 --- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -# See https://help.github.com/ignore-files/ for more about ignoring files. - -# dependencies -/node_modules - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -package-lock.json diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/package.json b/dxSampleReactReportingPrintWithoutPreview/ClientApp/package.json deleted file mode 100644 index 811faad..0000000 --- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/package.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "dxsamplereactreportingprintwithoutpreview", - "version": "0.1.0", - "private": true, - "dependencies": { - "@devexpress/analytics-core": "24.2-stable", - "bootstrap": "^4.1.3", - "devexpress-reporting": "24.2-stable", - "devextreme": "24.2-stable", - "devextreme-react": "24.2-stable", - "file-saver": "^2.0.2", - "jquery": "^3.4.1", - "merge": "^2.1.1", - "oidc-client": "^1.9.0", - "@testing-library/jest-dom": "^5.16.5", - "@testing-library/react": "^13.4.0", - "@testing-library/user-event": "^13.5.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-scripts": "5.0.1", - "react-router-bootstrap": "^0.26.2", - "react-router-dom": "^6.11.2", - "web-vitals": "^2.1.4" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "scripts": { - "start": "react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject" - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/public/favicon.ico b/dxSampleReactReportingPrintWithoutPreview/ClientApp/public/favicon.ico deleted file mode 100644 index a3a799985c43bc7309d701b2cad129023377dc71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32038 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 - - - - - - - - - - - - DevExpress Reporting Sample - - - - -
DevExpress Reporting Sample: Print Reports Without Preview
-
- - - diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/public/manifest.json b/dxSampleReactReportingPrintWithoutPreview/ClientApp/public/manifest.json deleted file mode 100644 index b50a0b1..0000000 --- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/public/manifest.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "short_name": "dxSampleReactReportingPrintWithoutPreview", - "name": "dxSampleReactReportingPrintWithoutPreview", - "icons": [ - { - "src": "favicon.ico", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/x-icon" - } - ], - "start_url": "./index.html", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" -} diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/App.test.js b/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/App.test.js deleted file mode 100644 index 2d26145..0000000 --- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/App.test.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { MemoryRouter } from 'react-router-dom'; -import App from './App'; - -it('renders without crashing', async () => { - const div = document.createElement('div'); - ReactDOM.render( - - - , div); - await new Promise(resolve => setTimeout(resolve, 1000)); -}); diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/custom.css b/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/custom.css deleted file mode 100644 index 24a6ac4..0000000 --- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/custom.css +++ /dev/null @@ -1,14 +0,0 @@ -/* Provide sufficient contrast against white background */ -a { - color: #0366d6; -} - -code { - color: #E01A76; -} - -.btn-primary { - color: #fff; - background-color: #1b6ec2; - border-color: #1861ac; -} diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/index.js b/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/index.js deleted file mode 100644 index 3ed9e66..0000000 --- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/index.js +++ /dev/null @@ -1,18 +0,0 @@ -import 'bootstrap/dist/css/bootstrap.css'; -import React from 'react'; -import ReactDOM from 'react-dom'; -import { BrowserRouter } from 'react-router-dom'; -import App from './App'; -import registerServiceWorker from './registerServiceWorker'; - -const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href'); -const rootElement = document.getElementById('root'); - -ReactDOM.render( - - - , - rootElement); - -registerServiceWorker(); - diff --git a/dxSampleReactReportingPrintWithoutPreview/Controllers/ReportingControllers.cs b/dxSampleReactReportingPrintWithoutPreview/Controllers/ReportingControllers.cs deleted file mode 100644 index 1b6e765..0000000 --- a/dxSampleReactReportingPrintWithoutPreview/Controllers/ReportingControllers.cs +++ /dev/null @@ -1,23 +0,0 @@ -using DevExpress.AspNetCore.Reporting.QueryBuilder; -using DevExpress.AspNetCore.Reporting.QueryBuilder.Native.Services; -using DevExpress.AspNetCore.Reporting.ReportDesigner; -using DevExpress.AspNetCore.Reporting.ReportDesigner.Native.Services; -using DevExpress.AspNetCore.Reporting.WebDocumentViewer; -using DevExpress.AspNetCore.Reporting.WebDocumentViewer.Native.Services; - -namespace dxSampleReactReportingPrintWithoutPreview.Controllers { - public class CustomWebDocumentViewerController : WebDocumentViewerController { - public CustomWebDocumentViewerController(IWebDocumentViewerMvcControllerService controllerService) : base(controllerService) { - } - } - - public class CustomReportDesignerController : ReportDesignerController { - public CustomReportDesignerController(IReportDesignerMvcControllerService controllerService) : base(controllerService) { - } - } - - public class CustomQueryBuilderController : QueryBuilderController { - public CustomQueryBuilderController(IQueryBuilderMvcControllerService controllerService) : base(controllerService) { - } - } -} diff --git a/dxSampleReactReportingPrintWithoutPreview/Pages/Error.cshtml b/dxSampleReactReportingPrintWithoutPreview/Pages/Error.cshtml deleted file mode 100644 index 09da0d2..0000000 --- a/dxSampleReactReportingPrintWithoutPreview/Pages/Error.cshtml +++ /dev/null @@ -1,26 +0,0 @@ -@page -@model ErrorModel -@{ - ViewData["Title"] = "Error"; -} - -

Error.

-

An error occurred while processing your request.

- -@if (Model.ShowRequestId) -{ -

- Request ID: @Model.RequestId -

-} - -

Development Mode

-

- Swapping to the Development environment displays detailed information about the error that occurred. -

-

- The Development environment shouldn't be enabled for deployed applications. - It can result in displaying sensitive information from exceptions to end users. - For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development - and restarting the app. -

diff --git a/dxSampleReactReportingPrintWithoutPreview/Pages/_ViewImports.cshtml b/dxSampleReactReportingPrintWithoutPreview/Pages/_ViewImports.cshtml deleted file mode 100644 index 3f481f3..0000000 --- a/dxSampleReactReportingPrintWithoutPreview/Pages/_ViewImports.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -@using dxSampleReactReportingPrintWithoutPreview -@namespace dxSampleReactReportingPrintWithoutPreview.Pages -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/dxSampleReactReportingPrintWithoutPreview/Program.cs b/dxSampleReactReportingPrintWithoutPreview/Program.cs deleted file mode 100644 index 68e6070..0000000 --- a/dxSampleReactReportingPrintWithoutPreview/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace dxSampleReactReportingPrintWithoutPreview -{ - public class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); - } -} diff --git a/dxSampleReactReportingPrintWithoutPreview/Startup.cs b/dxSampleReactReportingPrintWithoutPreview/Startup.cs deleted file mode 100644 index 0afbe04..0000000 --- a/dxSampleReactReportingPrintWithoutPreview/Startup.cs +++ /dev/null @@ -1,99 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpsPolicy; -using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using DevExpress.AspNetCore; -using DevExpress.AspNetCore.Reporting; -using Microsoft.AspNetCore.SpaServices.AngularCli; -using DevExpress.XtraReports.Web.Extensions; -using dxSampleReactReportingPrintWithoutPreview.Services; -using DevExpress.Security.Resources; -using System.IO; - -namespace dxSampleReactReportingPrintWithoutPreview -{ - public class Startup - { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - - services.AddDevExpressControls(); - services.AddScoped(); - services - .AddControllersWithViews() - .AddNewtonsoftJson(); - services.ConfigureReportingServices(configurator => { - configurator.ConfigureReportDesigner(designerConfigurator => { - designerConfigurator.RegisterDataSourceWizardConfigFileConnectionStringsProvider(); - }); - configurator.ConfigureWebDocumentViewer(viewerConfigurator => { - viewerConfigurator.UseCachedReportSourceBuilder(); - }); - }); - - // In production, the React files will be served from this directory - services.AddSpaStaticFiles(configuration => - { - configuration.RootPath = "ClientApp/build"; - }); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - var contentDirectoryAllowRule = DirectoryAccessRule.Allow(new DirectoryInfo(Path.Combine(env.ContentRootPath, "..", "Content")).FullName); - AccessSettings.ReportingSpecificResources.TrySetRules(contentDirectoryAllowRule, UrlAccessRule.Allow()); - - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseExceptionHandler("/Home/Error"); - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - app.UseHsts(); - } - - app.UseHttpsRedirection(); - app.UseStaticFiles(); - if (!env.IsDevelopment()) { - app.UseSpaStaticFiles(); - } - - app.UseRouting(); - - app.UseDevExpressControls(); - - System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls12; - - app.UseEndpoints(endpoints => - { - endpoints.MapControllerRoute( - name: "default", - pattern: "{controller}/{action=Index}/{id?}"); - }); - - app.UseSpa(spa => - { - spa.Options.SourcePath = "ClientApp"; - - if (env.IsDevelopment()) - { - spa.UseReactDevelopmentServer(npmScript: "start"); - } - }); - } - } -} diff --git a/dxSampleReactReportingPrintWithoutPreview/appsettings.Development.json b/dxSampleReactReportingPrintWithoutPreview/appsettings.Development.json deleted file mode 100644 index dba68eb..0000000 --- a/dxSampleReactReportingPrintWithoutPreview/appsettings.Development.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - } -} diff --git a/dxSampleReactReportingPrintWithoutPreview/appsettings.json b/dxSampleReactReportingPrintWithoutPreview/appsettings.json deleted file mode 100644 index b07cc57..0000000 --- a/dxSampleReactReportingPrintWithoutPreview/appsettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "ConnectionStrings": { - "NWindConnectionString": "XpoProvider=SQLite;Data Source=Data/nwind.db" - }, - "AllowedHosts": "*" -} diff --git a/dxSampleReactReportingPrintWithoutPreview/dxSampleReactReportingPrintWithoutPreview.csproj b/dxSampleReactReportingPrintWithoutPreview/dxSampleReactReportingPrintWithoutPreview.csproj deleted file mode 100644 index 10dd1a6..0000000 --- a/dxSampleReactReportingPrintWithoutPreview/dxSampleReactReportingPrintWithoutPreview.csproj +++ /dev/null @@ -1,60 +0,0 @@ - - - - net8.0 - true - Latest - false - ClientApp\ - $(DefaultItemExcludes);$(SpaRoot)node_modules\** - - - - - - - - - - - - - - - - - - - - - - TestReport.repx - - - TestReport.repx - - - - - - - - - - - - - - - - - - - - %(DistFiles.Identity) - PreserveNewest - true - - - - \ No newline at end of file diff --git a/react-app/.gitignore b/react-app/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/react-app/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/react-app/README.md b/react-app/README.md new file mode 100644 index 0000000..7059a96 --- /dev/null +++ b/react-app/README.md @@ -0,0 +1,12 @@ +# React + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project. diff --git a/react-app/eslint.config.js b/react-app/eslint.config.js new file mode 100644 index 0000000..cee1e2c --- /dev/null +++ b/react-app/eslint.config.js @@ -0,0 +1,29 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{js,jsx}'], + extends: [ + js.configs.recommended, + reactHooks.configs['recommended-latest'], + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + parserOptions: { + ecmaVersion: 'latest', + ecmaFeatures: { jsx: true }, + sourceType: 'module', + }, + }, + rules: { + 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }], + }, + }, +]) diff --git a/react-app/index.html b/react-app/index.html new file mode 100644 index 0000000..0c589ec --- /dev/null +++ b/react-app/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + + +
+ + + diff --git a/react-app/package.json b/react-app/package.json new file mode 100644 index 0000000..cc6580a --- /dev/null +++ b/react-app/package.json @@ -0,0 +1,29 @@ +{ + "name": "react-app", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.1.0", + "react-dom": "^19.1.0", + "file-saver": "^2.0.2", + "devexpress-reporting-react": "24.2-stable" + }, + "devDependencies": { + "@eslint/js": "^9.29.0", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "@vitejs/plugin-react": "^4.5.2", + "eslint": "^9.29.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.2.0", + "vite": "^7.0.0" + } +} diff --git a/react-app/public/vite.svg b/react-app/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/react-app/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/react-app/src/App.css b/react-app/src/App.css new file mode 100644 index 0000000..b9d355d --- /dev/null +++ b/react-app/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/App.js b/react-app/src/App.jsx similarity index 64% rename from dxSampleReactReportingPrintWithoutPreview/ClientApp/src/App.js rename to react-app/src/App.jsx index 30f1b77..7328c09 100644 --- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/App.js +++ b/react-app/src/App.jsx @@ -1,18 +1,19 @@ -import React, { Component } from 'react'; -import HomeComponent from "./components/HomeComponent"; - -import './custom.css' - -export default class App extends Component { - static displayName = App.name; - - render() { - return ( -
-
- -
-
- ); - } -} +import { useState, Component } from 'react' +import reactLogo from './assets/react.svg' +import viteLogo from '/vite.svg' +import './App.css' +import HomeComponent from "./components/HomeComponent"; + +export default class App extends Component { + static displayName = App.name; + + render() { + return ( +
+
+ +
+
+ ); + } +} diff --git a/react-app/src/assets/react.svg b/react-app/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/react-app/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/components/HomeComponent.jsx b/react-app/src/components/HomeComponent.jsx similarity index 98% rename from dxSampleReactReportingPrintWithoutPreview/ClientApp/src/components/HomeComponent.jsx rename to react-app/src/components/HomeComponent.jsx index d100eca..5af9f2d 100644 --- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/components/HomeComponent.jsx +++ b/react-app/src/components/HomeComponent.jsx @@ -18,59 +18,59 @@ class HomeComponent extends React.Component { }; onChange(event) { - this.selectedFormat = event.target.value; + this.selectedFormat = event.target.value; } - printInNewWindow() { - var frameElement = window.open("api/Home/Print", "_blank"); - frameElement.addEventListener("load", function (e) { - if (frameElement.document.contentType !== "text/html") - frameElement.print(); - }); + printInNewWindow() { + var frameElement = window.open("api/Home/Print", "_blank"); + frameElement.addEventListener("load", function (e) { + if (frameElement.document.contentType !== "text/html") + frameElement.print(); + }); } - printInIframe() { - var iframe = document.getElementById('printFrame'); - if (iframe.contentDocument.contentType !== "text/html") - iframe.contentWindow.print(); - } - - downloadFile() { - fetch("api/Home/Export?format=" + this.selectedFormat) - .then(response => response.blob()) - .then(data => { - saveAs(data, 'TestReport.' + this.selectedFormat.toLowerCase()); - }); - + printInIframe() { + var iframe = document.getElementById('printFrame'); + if (iframe.contentDocument.contentType !== "text/html") + iframe.contentWindow.print(); } - render() { + downloadFile() { + fetch("api/Home/Export?format=" + this.selectedFormat) + .then(response => response.blob()) + .then(data => { + saveAs(data, 'TestReport.' + this.selectedFormat.toLowerCase()); + }); - return ( -
- - - - - + } + + render() { + + return ( +
+ + + + +
); } diff --git a/react-app/src/index.css b/react-app/src/index.css new file mode 100644 index 0000000..08a3ac9 --- /dev/null +++ b/react-app/src/index.css @@ -0,0 +1,68 @@ +:root { + font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/react-app/src/main.jsx b/react-app/src/main.jsx new file mode 100644 index 0000000..1cc6021 --- /dev/null +++ b/react-app/src/main.jsx @@ -0,0 +1,14 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.jsx' +import registerServiceWorker from './registerServiceWorker' + + +createRoot(document.getElementById('root')).render( + + + , +) + +registerServiceWorker(); \ No newline at end of file diff --git a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/registerServiceWorker.js b/react-app/src/registerServiceWorker.js similarity index 97% rename from dxSampleReactReportingPrintWithoutPreview/ClientApp/src/registerServiceWorker.js rename to react-app/src/registerServiceWorker.js index da7c120..10b0baf 100644 --- a/dxSampleReactReportingPrintWithoutPreview/ClientApp/src/registerServiceWorker.js +++ b/react-app/src/registerServiceWorker.js @@ -1,108 +1,108 @@ -// In production, we register a service worker to serve assets from local cache. - -// This lets the app load faster on subsequent visits in production, and gives -// it offline capabilities. However, it also means that developers (and users) -// will only see deployed updates on the "N+1" visit to a page, since previously -// cached resources are updated in the background. - -// To learn more about the benefits of this model, read https://goo.gl/KwvDNy. -// This link also includes instructions on opting out of this behavior. - -const isLocalhost = Boolean( - window.location.hostname === 'localhost' || - // [::1] is the IPv6 localhost address. - window.location.hostname === '[::1]' || - // 127.0.0.1/8 is considered localhost for IPv4. - window.location.hostname.match( - /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ - ) -); - -export default function register () { - if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { - // The URL constructor is available in all browsers that support SW. - const publicUrl = new URL(process.env.PUBLIC_URL, window.location); - if (publicUrl.origin !== window.location.origin) { - // Our service worker won't work if PUBLIC_URL is on a different origin - // from what our page is served on. This might happen if a CDN is used to - // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 - return; - } - - window.addEventListener('load', () => { - const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; - - if (isLocalhost) { - // This is running on localhost. Lets check if a service worker still exists or not. - checkValidServiceWorker(swUrl); - } else { - // Is not local host. Just register service worker - registerValidSW(swUrl); - } - }); - } -} - -function registerValidSW (swUrl) { - navigator.serviceWorker - .register(swUrl) - .then(registration => { - registration.onupdatefound = () => { - const installingWorker = registration.installing; - installingWorker.onstatechange = () => { - if (installingWorker.state === 'installed') { - if (navigator.serviceWorker.controller) { - // At this point, the old content will have been purged and - // the fresh content will have been added to the cache. - // It's the perfect time to display a "New content is - // available; please refresh." message in your web app. - console.log('New content is available; please refresh.'); - } else { - // At this point, everything has been precached. - // It's the perfect time to display a - // "Content is cached for offline use." message. - console.log('Content is cached for offline use.'); - } - } - }; - }; - }) - .catch(error => { - console.error('Error during service worker registration:', error); - }); -} - -function checkValidServiceWorker (swUrl) { - // Check if the service worker can be found. If it can't reload the page. - fetch(swUrl) - .then(response => { - // Ensure service worker exists, and that we really are getting a JS file. - if ( - response.status === 404 || - response.headers.get('content-type').indexOf('javascript') === -1 - ) { - // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then(registration => { - registration.unregister().then(() => { - window.location.reload(); - }); - }); - } else { - // Service worker found. Proceed as normal. - registerValidSW(swUrl); - } - }) - .catch(() => { - console.log( - 'No internet connection found. App is running in offline mode.' - ); - }); -} - -export function unregister () { - if ('serviceWorker' in navigator) { - navigator.serviceWorker.ready.then(registration => { - registration.unregister(); - }); - } -} +// In production, we register a service worker to serve assets from local cache. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on the "N+1" visit to a page, since previously +// cached resources are updated in the background. + +// To learn more about the benefits of this model, read https://goo.gl/KwvDNy. +// This link also includes instructions on opting out of this behavior. + +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.1/8 is considered localhost for IPv4. + window.location.hostname.match( + /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ + ) +); + +export default function register () { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 + return; + } + + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + + if (isLocalhost) { + // This is running on localhost. Lets check if a service worker still exists or not. + checkValidServiceWorker(swUrl); + } else { + // Is not local host. Just register service worker + registerValidSW(swUrl); + } + }); + } +} + +function registerValidSW (swUrl) { + navigator.serviceWorker + .register(swUrl) + .then(registration => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the old content will have been purged and + // the fresh content will have been added to the cache. + // It's the perfect time to display a "New content is + // available; please refresh." message in your web app. + console.log('New content is available; please refresh.'); + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + } + } + }; + }; + }) + .catch(error => { + console.error('Error during service worker registration:', error); + }); +} + +function checkValidServiceWorker (swUrl) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl) + .then(response => { + // Ensure service worker exists, and that we really are getting a JS file. + if ( + response.status === 404 || + response.headers.get('content-type').indexOf('javascript') === -1 + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then(registration => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl); + } + }) + .catch(() => { + console.log( + 'No internet connection found. App is running in offline mode.' + ); + }); +} + +export function unregister () { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready.then(registration => { + registration.unregister(); + }); + } +} diff --git a/react-app/vite.config.js b/react-app/vite.config.js new file mode 100644 index 0000000..9d1433d --- /dev/null +++ b/react-app/vite.config.js @@ -0,0 +1,18 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], + server: { + https: false, // Vite dev server remains on HTTP + proxy: { + '/api': { + target: 'https://localhost:44335', + changeOrigin: true, + secure: false, // allow self-signed SSL cert + } + } + } +}) \ No newline at end of file From d263207464d376dfffd6cdca9509ede01ebdf8af Mon Sep 17 00:00:00 2001 From: DevExpressExampleBot Date: Tue, 1 Jul 2025 14:17:54 +0300 Subject: [PATCH 02/12] Created a new file CODEOWNERS [skip ci] --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 CODEOWNERS diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..a88e69e --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @DevExpressExampleBot \ No newline at end of file From 53d1c431fa356ca4b0f54f18db64a31e1e4344e2 Mon Sep 17 00:00:00 2001 From: DevExpressExampleBot Date: Tue, 1 Jul 2025 15:27:58 +0400 Subject: [PATCH 03/12] README auto update [skip ci] --- Readme.md | 1 - 1 file changed, 1 deletion(-) diff --git a/Readme.md b/Readme.md index f759d91..aa627c5 100644 --- a/Readme.md +++ b/Readme.md @@ -1,5 +1,4 @@ -![](https://img.shields.io/endpoint?url=https://codecentral.devexpress.com/api/v1/VersionRange/274919030/24.2.1%2B) [![](https://img.shields.io/badge/Open_in_DevExpress_Support_Center-FF7200?style=flat-square&logo=DevExpress&logoColor=white)](https://supportcenter.devexpress.com/ticket/details/T902911) [![](https://img.shields.io/badge/📖_How_to_use_DevExpress_Examples-e9f6fc?style=flat-square)](https://docs.devexpress.com/GeneralInformation/403183) [![](https://img.shields.io/badge/💬_Leave_Feedback-feecdd?style=flat-square)](#does-this-example-address-your-development-requirementsobjectives) From 6bd23a7a2e190dc7f97eb4d2966b56697f5045e9 Mon Sep 17 00:00:00 2001 From: Polina Tyureva Date: Tue, 1 Jul 2025 15:29:48 +0400 Subject: [PATCH 04/12] update example --- .gitignore | 1 - Images/screenshot.png | Bin 14277 -> 31334 bytes Readme.md | 32 +++++++++++---------- ServerApp/Properties/launchSettings.json | 12 ++++++++ react-app/src/components/HomeComponent.jsx | 6 ++-- 5 files changed, 32 insertions(+), 19 deletions(-) create mode 100644 ServerApp/Properties/launchSettings.json diff --git a/.gitignore b/.gitignore index 74e009e..2bdfa04 100644 --- a/.gitignore +++ b/.gitignore @@ -48,7 +48,6 @@ BenchmarkDotNet.Artifacts/ project.lock.json project.fragment.lock.json artifacts/ -**/Properties/launchSettings.json *_i.c *_p.c diff --git a/Images/screenshot.png b/Images/screenshot.png index de5b3e866ce0fee55ac7ba8610dd6aaba1c8ca84..37ce5a286159d59d4050eba56a454987822dbd8a 100644 GIT binary patch literal 31334 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 diff --git a/Readme.md b/Readme.md index f759d91..44434d5 100644 --- a/Readme.md +++ b/Readme.md @@ -10,30 +10,32 @@ This example prints and exports a report in a browser without previewing it on a ![Screenshot](Images/screenshot.png) -## Print +## Export Operations + +The user selects the format from the drop-down list and clicks the **Export the report** button to send the format to the server-side controller. The controller calls the export method for the selected format and sends the file back to the browser. + +## Print Operations On the **server side**, a controller performs the following actions: -- [creates a report](https://docs.devexpress.com/XtraReports/2440/get-started-with-devexpress-reporting/create-a-report-from-a-to-z); -- [exports the report to PDF](https://docs.devexpress.com/XtraReports/2574/detailed-guide-to-devexpress-reporting/store-and-distribute-reports/export-reports/export-to-pdf) with the [XtraReport.ExportToPdfAsync](https://docs.devexpress.com/XtraReports/DevExpress.XtraReports.UI.XtraReport.ExportToPdfAsync.overloads) method; +- [creates a report](https://docs.devexpress.com/XtraReports/2440/get-started-with-devexpress-reporting/create-a-report-from-a-to-z) +- [exports the report to PDF](https://docs.devexpress.com/XtraReports/2574/detailed-guide-to-devexpress-reporting/store-and-distribute-reports/export-reports/export-to-pdf) with the [XtraReport.ExportToPdfAsync](https://docs.devexpress.com/XtraReports/DevExpress.XtraReports.UI.XtraReport.ExportToPdfAsync.overloads) method - sends the PDF file back to the client. On the **client-side**, a user can do one of the following: -* Print a report in a new tab. -Click a button to call the client-side `window.Open(url, "_blank")` method to open a new window that contains a PDF file and print the window content. +* Print a report in a new tab -* Print a report in iFrame. -Click a button to load a PDF file in the `HTMLIFrameElement` and print its content. + Click the **Print the report in a new tab** button to call the client-side `window.Open(url, "_blank")` method to open a new window that contains a PDF file and print the window content. +* Print a report in iFrame -## Export + Click the **Print the report in iFrame** button to load a PDF file in the `HTMLIFrameElement` and print its content. -The user selects the format and clicks a button to send the format to the server-side controller. The controller calls the export method for the selected format and sends the file back to the browser. ## Files to Review -- [HomeComponent.jsx](dxSampleReactReportingPrintWithoutPreview/ClientApp/src/components/HomeComponent.jsx) -- [HomeController.cs](dxSampleReactReportingPrintWithoutPreview/Controllers/HomeController.cs) +- [HomeComponent.jsx](react-app/src/components/HomeComponent.jsx) +- [HomeController.cs](ServerApp/Controllers/HomeController.cs) ## Documentation @@ -46,9 +48,9 @@ The user selects the format and clicks a button to send the format to the server - [Reporting for Web (React) - Document Viewer](https://github.com/DevExpress-Examples/reporting-document-viewer-in-javascript-with-react) - [How to Print and Export a Report in the ASP.NET Core Application without the Document Viewer](https://github.com/DevExpress-Examples/Reporting-AspNetCore-Print-Without-Preview) -## Does this example address your development requirements/objectives? - -[](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=reporting-react-print-without-preview&~~~was_helpful=yes) [](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=reporting-react-print-without-preview&~~~was_helpful=no) - +## Does this example address your development requirements/objectives? + +[](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=reporting-react-print-without-preview&~~~was_helpful=yes) [](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=reporting-react-print-without-preview&~~~was_helpful=no) + (you will be redirected to DevExpress.com to submit your response) diff --git a/ServerApp/Properties/launchSettings.json b/ServerApp/Properties/launchSettings.json new file mode 100644 index 0000000..289cc86 --- /dev/null +++ b/ServerApp/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "ServerApp": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:44335;http://localhost:13781" + } + } +} diff --git a/react-app/src/components/HomeComponent.jsx b/react-app/src/components/HomeComponent.jsx index 5af9f2d..50fd523 100644 --- a/react-app/src/components/HomeComponent.jsx +++ b/react-app/src/components/HomeComponent.jsx @@ -61,13 +61,13 @@ class HomeComponent extends React.Component { From c992aa139da3c6cf259883fb5af00d584be96c3a Mon Sep 17 00:00:00 2001 From: DevExpressExampleBot Date: Tue, 1 Jul 2025 15:31:59 +0400 Subject: [PATCH 05/12] README auto update [skip ci] --- Readme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Readme.md b/Readme.md index 2904ef4..98a5547 100644 --- a/Readme.md +++ b/Readme.md @@ -47,9 +47,9 @@ On the **client-side**, a user can do one of the following: - [Reporting for Web (React) - Document Viewer](https://github.com/DevExpress-Examples/reporting-document-viewer-in-javascript-with-react) - [How to Print and Export a Report in the ASP.NET Core Application without the Document Viewer](https://github.com/DevExpress-Examples/Reporting-AspNetCore-Print-Without-Preview) -## Does this example address your development requirements/objectives? - -[](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=reporting-react-print-without-preview&~~~was_helpful=yes) [](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=reporting-react-print-without-preview&~~~was_helpful=no) - +## Does this example address your development requirements/objectives? + +[](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=reporting-react-print-without-preview&~~~was_helpful=yes) [](https://www.devexpress.com/support/examples/survey.xml?utm_source=github&utm_campaign=reporting-react-print-without-preview&~~~was_helpful=no) + (you will be redirected to DevExpress.com to submit your response) From 3335e7f09c6fac80968b6c58cef3e7e17139309b Mon Sep 17 00:00:00 2001 From: Polina Tyureva Date: Tue, 1 Jul 2025 15:35:47 +0400 Subject: [PATCH 06/12] update readme --- Readme.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index 2904ef4..0dc70f6 100644 --- a/Readme.md +++ b/Readme.md @@ -9,11 +9,30 @@ This example prints and exports a report in a browser without previewing it on a ![Screenshot](Images/screenshot.png) -## Export Operations +## Run the Project + +Run the following command from the *ServerApp* folder: + +```cmd +dotnet run +``` + +Run the following commands from the *react-app* folder: + +```cmd +npm install +mpm run dev +``` + +Open your browser and navigate to the URL specified in the command output to see the result. + +## Implementation Details + +### Export Operations The user selects the format from the drop-down list and clicks the **Export the report** button to send the format to the server-side controller. The controller calls the export method for the selected format and sends the file back to the browser. -## Print Operations +### Print Operations On the **server side**, a controller performs the following actions: - [creates a report](https://docs.devexpress.com/XtraReports/2440/get-started-with-devexpress-reporting/create-a-report-from-a-to-z) From 2715b51a9d92a0dda62c1040eb93d796b522a9c3 Mon Sep 17 00:00:00 2001 From: Polina Tyureva Date: Tue, 1 Jul 2025 18:42:51 +0400 Subject: [PATCH 07/12] upd --- react-app/src/components/HomeComponent.jsx | 123 +++++++++------------ 1 file changed, 51 insertions(+), 72 deletions(-) diff --git a/react-app/src/components/HomeComponent.jsx b/react-app/src/components/HomeComponent.jsx index 50fd523..9b45fec 100644 --- a/react-app/src/components/HomeComponent.jsx +++ b/react-app/src/components/HomeComponent.jsx @@ -1,85 +1,64 @@ -import React from "react"; -import saveAs from "file-saver"; +import React, { useRef, useState } from 'react'; +import { saveAs } from 'file-saver'; +export default function HomeComponent() { + let selectedFormat = 'PDF'; -class HomeComponent extends React.Component { - constructor(props) { - super(props); - - this.selectedFormat = 'pdf'; - this.printUrl = ""; - - this.reportUrl = "TestReport"; - - this.onChange = this.onChange.bind(this); - this.downloadFile = this.downloadFile.bind(this); - this.printInNewWindow = this.printInNewWindow.bind(this); - this.printInIframe = this.printInIframe.bind(this); + const downloadFile = () => { + fetch(`/api/Home/Export?format=${selectedFormat}`) + .then(response => response.blob()) + .then(data => { + saveAs(data, 'TestReport.' + selectedFormat.toLowerCase()); + }) + .catch(error => { + console.error('An error has occurred:', error); + }); }; - onChange(event) { - this.selectedFormat = event.target.value; - } - - printInNewWindow() { + const printInNewTab = () => { var frameElement = window.open("api/Home/Print", "_blank"); frameElement.addEventListener("load", function (e) { if (frameElement.document.contentType !== "text/html") frameElement.print(); }); - } - - printInIframe() { - var iframe = document.getElementById('printFrame'); - if (iframe.contentDocument.contentType !== "text/html") - iframe.contentWindow.print(); - } - - downloadFile() { - fetch("api/Home/Export?format=" + this.selectedFormat) - .then(response => response.blob()) - .then(data => { - saveAs(data, 'TestReport.' + this.selectedFormat.toLowerCase()); - }); + }; - } + const printInIframe = () => { + const iframe = document.getElementById('printFrame'); + if (!iframe) { + console.error('IFrame not found'); + return; + } + try { + if (iframe.contentDocument?.contentType !== "text/html") { + iframe.contentWindow.print(); + } + } catch (error) { + console.error('An error has occurred::', error); + } + }; - render() { - return ( -

- selectedFormat = (e.target.value)}> + + + + + + + + + + - - - - -
- ); - } - componentDidMount() { - - } - componentWillUnmount() { - - } -}; - -export default HomeComponent; + + + +
+