Initial commit: JDE Scoping Tool migration project

Set up repository with legacy .NET Framework 4.8 source (OLD/),
new .NET 10 Blazor solution (NEW/), OpenSpec specifications,
documentation, and project configuration.
This commit is contained in:
Joseph Doherty
2026-01-02 07:43:29 -05:00
commit 26ff8d9b4f
1761 changed files with 596509 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
@model WebInterface.Models.LogonRequest
@{
ViewBag.Title = "Login";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Authentication Required</h2>
@using (Html.BeginForm())
{
<div class="form-horizontal">
<hr />
@foreach (var error in ViewData.ModelState.Keys.SelectMany(k => ViewData.ModelState[k].Errors))
{
<div class="alert alert-danger" role="alert">
@error.ErrorMessage
</div>
}
@Html.HiddenFor(model => model.RedirectURL)
<div class="form-group">
@Html.LabelFor(model => model.UserName, "Username", htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.TextBoxFor(model => model.UserName, new { @class = "form-control", style = "width: 400px;" })
@Html.ValidationMessageFor(model => model.UserName, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Password, "Password", htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.PasswordFor(model => model.Password, new { @class = "form-control", style = "width: 400px;" })
@Html.ValidationMessageFor(model => model.Password, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Login" class="btn btn-primary" />
</div>
</div>
</div>
}
+11
View File
@@ -0,0 +1,11 @@
@{
ViewBag.Title = "Authorization Error";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Authorization Error</h2>
<div class="alert alert-danger" role="alert">
You are not authorized to use the resource at <b><a href="@(ViewBag.ResourceURL)">@(ViewBag.ResourceURL)</a></b>.
</div>
+8
View File
@@ -0,0 +1,8 @@
@{
ViewBag.Title = "Unapproved Browser";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Your browser is not on the approved browser list. Only Internet Explorer and Chrome are approved for accessing this site.</h2>
+123
View File
@@ -0,0 +1,123 @@
@model WebInterface.Models.RefreshStatusModel
@{
ViewBag.Title = "Cache Refresh Status";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<style>
.table tbody tr th {
vertical-align: bottom;
text-align: center;
}
.table tbody tr td {
vertical-align: middle;
text-align: center;
}
</style>
<div class="form-horizontal panel panel-default" style="margin-top: 25px;">
<div class="panel-heading">
@ViewBag.Title
</div>
@using (Html.BeginForm())
{
<div class="panel-body container">
<div class="col-md-4">
@Html.LabelFor(model => model.MinDT, "Start Time", new { @class = "control-label" })
@Html.EditorFor(model => model.MinDT, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.MinDT, "", new { @class = "text-danger" })
</div>
<div class="col-md-4">
@Html.LabelFor(model => model.MaxDT, "End Time", new { @class = "control-label" })
@Html.EditorFor(model => model.MaxDT, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.MaxDT, "", new { @class = "text-danger" })
</div>
<div class="col-md-2">
<input type="submit" value="Filter" class="btn btn-default" />
</div>
</div>
}
</div>
<table class="table">
<tr>
<th rowspan="2">Start</th>
<th rowspan="2">End</th>
<th colspan="12">Number Of Records Updated</th>
<th rowspan="2">
Was Successful?
</th>
</tr>
<tr>
<th>Branch</th>
<th>Profit Center</th>
<th>Work Center</th>
<th>Org Hierarchy</th>
<th>Status Code</th>
<th>User</th>
<th>Item</th>
<th>Lot</th>
<th>Work Order</th>
<th>Work Order Step</th>
<th>Work Order Component</th>
</tr>
@foreach (var item in Model.Results.OrderByDescending(i => i.StartDT))
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.StartDT)
</td>
<td>
@Html.DisplayFor(modelItem => item.EndDT)
</td>
<td>
@Html.DisplayFor(modelItem => item.BranchRecords)
</td>
<td>
@Html.DisplayFor(modelItem => item.ProfitCenterRecords)
</td>
<td>
@Html.DisplayFor(modelItem => item.WorkCenterRecords)
</td>
<td>
@Html.DisplayFor(modelItem => item.OrgHierarchyRecords)
</td>
<td>
@Html.DisplayFor(modelItem => item.StatusCodeRecords)
</td>
<td>
@Html.DisplayFor(modelItem => item.UserRecords)
</td>
<td>
@Html.DisplayFor(modelItem => item.ItemRecords)
</td>
<td>
@Html.DisplayFor(modelItem => item.LotRecords)
</td>
<td>
@Html.DisplayFor(modelItem => item.WorkOrderRecords)
</td>
<td>
@Html.DisplayFor(modelItem => item.WorkOrderStepRecords)
</td>
<td>
@Html.DisplayFor(modelItem => item.WorkOrderComponentRecords)
</td>
@if (item.WasSuccessful)
{
<td class="bg-success">YES</td>
}
else
{
<td class="bg-danger">NO</td>
}
</tr>
}
</table>
File diff suppressed because it is too large Load Diff
+118
View File
@@ -0,0 +1,118 @@
@using WebInterface.Helpers
@{
ViewBag.Title = "Searches";
Layout = "~/Views/Shared/_Layout.cshtml";
}
@Scripts.Render("~/Scripts/kendoHelpers.js")
@Scripts.Render("~/Scripts/model/models.js")
@Scripts.Render("~/Scripts/jquery.signalR-2.2.1.js")
<script src="~/signalr/hubs"></script>
<h2>
@ViewBag.Title
<span class="btn-group" style="padding-left: 25px;">
@Html.ActionLink("Start New Search", "Create", null, new { @class = "btn btn-sm btn-primary", target = "_blank" })
@Html.ActionLink("View Search Queue", "Queue", null, new { @class = "btn btn-sm btn-default", target = "_blank" })
</span>
</h2>
<div id="sessionsGrid"
data-role="grid"
data-editable="false"
data-sortable="{ 'mode' : 'multiple', 'allowUnsort' : true}"
data-scrollage="true"
data-pageable="{ 'pagesize' : 10 }"
data-pagesize="10"
data-bind="source: sessions"
data-columns="[
{ 'field': 'Name' },
{ 'field': 'SubmitDT', 'title': 'Submitted', 'template': kendo.template($('#submitDTTemplate').html()) },
{ 'field': 'Status' },
{ 'command' : [ { 'name' : 'viewSearch', 'text': 'View', 'click': viewModel.ViewSearch} ], 'title': 'Actions', 'width': '100px'}
]"
style="min-height: 450px;"></div>
<script type="text/x-kendo-template" id="submitDTTemplate">
#= SubmitDT != null ? kendo.toString(kendo.parseDate(SubmitDT), 'MM/dd/yyyy hh:mm:ss tt') : '' #
</script>
<script type="text/javascript">
var statusHub = $.connection.statusHub;
statusHub.client.searchUpdate = function (searchUpdate) {
viewModel.processUpdate(searchUpdate);
};
window.hubReady = $.connection.hub.start().done(function () {
});
$.connection.hub.disconnected(function () {
setTimeout(function() {
$.connection.hub.start();
}, 5000); // Restart connection after 5 seconds.
});
var viewModel = kendo.observable({
sessions: new kendo.data.DataSource({
pageSize: 10,
sort: { field: "SubmitDT", dir: "desc" },
schema: {
model: {
id: 'ID'
}
}
}),
setData: function (data) {
this.sessions.data(data);
kendo.ui.progress($('#sessionsGrid'), false);
},
processUpdate: function (searchUpdate) {
if (searchUpdate.UserName !== '@(Html.CurrentUser().Username)') {
return;
}
var ds = this.get('sessions');
var existing = ds.get(searchUpdate.ID);
if (existing) {
ds.pushUpdate(searchUpdate);
} else {
ds.pushCreate(searchUpdate);
}
},
ViewSearch: function (e) {
e.preventDefault();
//Open search window
var dataItem = this.dataItem($(e.currentTarget).closest("tr"));
var url = '@Url.Action("Create","Search")?id=' + dataItem.ID;
window.open(url, '_blank').focus();
}
});
$(document).ready(function () {
$.ajaxSetup({ cache: false });
//Find grid container
var sessionsGrid = $('#sessionsGrid');
//Bind data model
kendo.bind(sessionsGrid, viewModel);
//Display loading indicator
kendo.ui.progress(sessionsGrid, true);
//Handle row double clicks
kendoHelpers.grid.eventRowDoubleClick($('#sessionsGrid').data('kendoGrid'),
function (dataItem) {
//Open session setup window
var url = '@Url.Action("Create","Search")?id=' + dataItem.ID;
window.open(url, '_blank').focus();
});
//Load sessions data
$.getJSON('@Url.Action("GetSearches", "Search")',
null,
function (results) {
viewModel.setData(results);
});
});
</script>
+143
View File
@@ -0,0 +1,143 @@
@{
ViewBag.Title = "Search Queue";
Layout = "~/Views/Shared/_Layout.cshtml";
}
@Scripts.Render("~/Scripts/kendoHelpers.js")
@Scripts.Render("~/Scripts/model/models.js")
@Scripts.Render("~/Scripts/jquery.signalR-2.2.1.js")
<script src="~/signalr/hubs"></script>
<h2>
@ViewBag.Title
</h2>
<div id="container">
<div class="panel panel-default" style="margin-top: 25px;">
<div class="panel-heading">
Search Processor Status
</div>
<div class="panel-body container">
<div class="form-group col-md-8">
<label for="statusMessageTB">Status Message</label>
<input type="text" class="form-control" id="statusMessageTB" name="statusMessageTB" readonly data-bind="value: statusMessage">
</div>
<div class="form-group col-md-4">
<label for="statusUpdateDTTB">Last Update Timestamp</label>
<input type="text" class="form-control" id="statusUpdateDTTB" name="statusUpdateDTTB" readonly data-bind="value: statusUpdateDT">
</div>
</div>
</div>
<div id="sessionsGrid"
data-role="grid"
data-editable="false"
data-sortable="{ 'mode' : 'multiple', 'allowUnsort' : true}"
data-scrollage="true"
data-pageable="{ 'pagesize' : 20 }"
data-pagesize="20"
data-bind="source: sessions"
data-columns="[
{ 'field': 'UserName', 'title': 'Owner' },
{ 'field': 'Name' },
{ 'field': 'SubmitDT', 'title': 'Submitted', 'template': kendo.template($('#submitDTTemplate').html()) },
{ 'field': 'StartDT', 'title': 'Started', 'template': kendo.template($('#startDTTemplate').html()) },
{ 'field': 'EndDT', 'title': 'Ended', 'template': kendo.template($('#endDTTemplate').html()) },
{ 'field': 'Status' }
]"
style="min-height: 450px;"></div>
</div>
<script type="text/x-kendo-template" id="submitDTTemplate">
#= SubmitDT != null ? kendo.toString(kendo.parseDate(SubmitDT), 'MM/dd/yyyy hh:mm:ss tt') : '' #
</script>
<script type="text/x-kendo-template" id="startDTTemplate">
#= StartDT != null ? kendo.toString(kendo.parseDate(StartDT), 'MM/dd/yyyy hh:mm:ss tt') : '' #
</script>
<script type="text/x-kendo-template" id="endDTTemplate">
#= EndDT != null ? kendo.toString(kendo.parseDate(EndDT), 'MM/dd/yyyy hh:mm:ss tt') : '' #
</script>
<script type="text/javascript">
var viewModel = kendo.observable({
statusMessage: null,
statusUpdateDT: null,
sessions: new kendo.data.DataSource({
pageSize: 20,
sort: { field: "SubmitDT", dir: "asc" },
schema: {
model: {
id: 'ID'
}
}
}),
setData: function (data) {
this.sessions.data(data);
kendo.ui.progress($('#sessionsGrid'), false);
},
processUpdate: function (searchUpdate) {
var ds = this.get('sessions');
if (searchUpdate.Status === 'Ended' || searchUpdate.Status === 'Error') {
//Search completed => remove from list
ds.pushDestroy(searchUpdate);
} else {
//Update display from search update
var existing = ds.get(searchUpdate.ID);
if (existing) {
ds.pushUpdate(searchUpdate);
} else {
ds.pushCreate(searchUpdate);
}
}
},
processStatus: function (statusUpdate) {
var statusMessage = getValue(statusUpdate.Message, null);
var statusUpdateDT = getValue(statusUpdate.Timestamp, null);
this.set('statusMessage', statusMessage);
this.set('statusUpdateDT', statusUpdateDT != null ? kendo.toString(kendo.parseDate(statusUpdateDT), 'MM/dd/yyyy hh:mm:ss tt') : null);
}
});
var statusHub = $.connection.statusHub;
statusHub.client.statusUpdate = function (statusUpdate) {
viewModel.processStatus(statusUpdate);
};
statusHub.client.searchUpdate = function (searchUpdate) {
viewModel.processUpdate(searchUpdate);
};
window.hubReady = $.connection.hub.start().done(function () {
statusHub.server.getCachedStatus().done(function (cachedStatus) {
viewModel.processStatus(cachedStatus);
});
});
$.connection.hub.disconnected(function () {
setTimeout(function() {
$.connection.hub.start();
}, 5000); // Restart connection after 5 seconds.
});
$(document).ready(function () {
$.ajaxSetup({ cache: false });
//Find grid container
var sessionsGrid = $('#sessionsGrid');
//Bind data model
kendo.bind($('#container'), viewModel);
//Display loading indicator
kendo.ui.progress(sessionsGrid, true);
//Load sessions data
$.getJSON('@Url.Action("GetQueue", "Search")',
null,
function (results) {
viewModel.setData(results);
});
});
</script>
@@ -0,0 +1,8 @@
@model decimal?
@(Html.Kendo().CurrencyTextBoxFor(m => m)
.HtmlAttributes(new {style="width:100%"})
.Min(0)
)
@@ -0,0 +1,3 @@
@model DateTime?
@(Html.Kendo().DatePickerFor(m => m))
@@ -0,0 +1,3 @@
@model DateTime?
@(Html.Kendo().DateTimePickerFor(m => m))
@@ -0,0 +1,3 @@
@model object
@Html.TextBoxFor(model => model, new {@class="k-textbox" })
@@ -0,0 +1,3 @@
@model object
@Html.TextBoxFor(model => model, new {@class="k-textbox", type="email" })
@@ -0,0 +1,6 @@
@model object
@(
Html.Kendo().DropDownListFor(m => m)
.BindTo((SelectList)ViewData[ViewData.TemplateInfo.GetFullHtmlFieldName("") + "_Data"])
)
@@ -0,0 +1,7 @@
@model int?
@(Html.Kendo().IntegerTextBoxFor(m => m)
.HtmlAttributes(new { style = "width:100%" })
.Min(int.MinValue)
.Max(int.MaxValue)
)
@@ -0,0 +1,5 @@
@model double?
@(Html.Kendo().NumericTextBoxFor(m => m)
.HtmlAttributes(new { style = "width:100%" })
)
@@ -0,0 +1,3 @@
@model object
@Html.TextBoxFor(model => model, new {@class="k-textbox", type="password" })
@@ -0,0 +1,3 @@
@model object
@Html.Kendo().TextBoxFor(model => model)
@@ -0,0 +1,3 @@
@model DateTime?
@(Html.Kendo().TimePickerFor(m => m))
+3
View File
@@ -0,0 +1,3 @@
@model object
@Html.TextBoxFor(model => model, new {@class="k-textbox", type="url" })
+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Error</title>
</head>
<body>
<hgroup>
<h1>Error.</h1>
<h2>An error occurred while processing your request.</h2>
</hgroup>
</body>
</html>
+60
View File
@@ -0,0 +1,60 @@
@using WebInterface.Helpers
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title - JDE Scoping Tool</title>
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
@Scripts.Render("~/bundles/jquery")
<link href="@Url.Content("~/Content/kendo/2019.2.619/kendo.common-bootstrap.min.css")" rel="stylesheet" type="text/css" />
<link href="@Url.Content("~/Content/kendo/2019.2.619/kendo.mobile.all.min.css")" rel="stylesheet" type="text/css" />
<link href="@Url.Content("~/Content/kendo/2019.2.619/kendo.dataviz.min.css")" rel="stylesheet" type="text/css" />
<link href="@Url.Content("~/Content/kendo/2019.2.619/kendo.bootstrap.min.css")" rel="stylesheet" type="text/css" />
<link href="@Url.Content("~/Content/kendo/2019.2.619/kendo.dataviz.bootstrap.min.css")" rel="stylesheet" type="text/css" />
<link href="@Url.Content("~/Content/bootstrap.min.css")" rel="stylesheet" type="text/css" />
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/kendo/2019.2.619/jquery.min.js")"></script>
<script src="@Url.Content("~/Scripts/kendo/2019.2.619/jszip.min.js")"></script>
<script src="@Url.Content("~/Scripts/kendo/2019.2.619/kendo.all.min.js")"></script>
<script src="@Url.Content("~/Scripts/kendo/2019.2.619/kendo.aspnetmvc.min.js")"></script>
<script src="@Url.Content("~/Scripts/bootstrap.min.js")"></script>
<script src="@Url.Content("~/Scripts/kendo.modernizr.custom.js")"></script>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
@Html.ActionLink("JDE Scoping Tool", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="nav navbar-nav">
<p class="navbar-btn">
@Html.LogonControl()
</p>
</li>
</ul>
</div>
</div>
</div>
<div class="container body-content">
@RenderBody()
<hr />
<footer>JDE Scoping Tool Version 4</footer>
</div>
@Scripts.Render("~/bundles/bootstrap")
@RenderSection("scripts", required: false)
</body>
</html>
+3
View File
@@ -0,0 +1,3 @@
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
+44
View File
@@ -0,0 +1,44 @@
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
</sectionGroup>
</configSections>
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Optimization" />
<add namespace="System.Web.Routing" />
<add namespace="WebInterface" />
<add namespace="Kendo.Mvc.UI" />
</namespaces>
</pages>
</system.web.webPages.razor>
<appSettings>
<add key="webpages:Enabled" value="false" />
</appSettings>
<system.webServer>
<handlers>
<remove name="BlockViewHandler" />
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
</system.webServer>
<system.web>
<compilation>
<assemblies>
<add assembly="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</assemblies>
</compilation>
</system.web>
</configuration>