v1.3.0 Restructure Saxonite workspace and upgrade to .NET 10 (#1)

Move Saxonite’s projects into a dedicated `Workspace` folder and align the solution with .NET 10 and the current Common workspace. Update encryption integration, test project structure and coverage, and repository guidance and upgrade notes.

- Relocate application and library projects while retaining Git rename history.
- Split tests into Admin, Online, and Shared projects; add representative behavioral and serialization tests.
- Update Common integration, application version, and repository documentation.

Application version: 1.3.0

Reviewed-on: #1
Co-authored-by: andy <andy@sarand.com>
This commit was merged in pull request #1.
This commit is contained in:
2026-10-16 18:24:16 +01:00
committed by andy
parent 2c3fb146ab
commit 9f22b522df
1996 changed files with 1992 additions and 1179 deletions
@@ -0,0 +1,50 @@
@model SaxModel_Admin
@inject SaxLocaliser Localiser
@{
// Ensure view is only included once in page.
if (!Model.RegisterView(ViewContext)) return;
// Scripts.
Model.AdditionalFiles_JS.Add("js/sax-cnt-mth.js");
}
<div id="dlgContactMethod" class="sax-dialog sax-form" title="@Localiser["caption.contact.method"]">
<div class="spacer4"></div>
@* Method *@
<div class="sax-form-control ContactMethod">
<div class="row">
<div class="col-sm-3">
<div class="sax-label">
<label for=""><span>@Localiser["label.method"]</span></label>
</div>
</div>
<div class="col-sm">
<div class="sax-control">
<select name="ContactMethod" class="ContactMethod sax-select" style="max-width:240px;" ></select>
</div>
<div class="sax-message"></div>
</div>
</div>
</div>
<div class="spacer"></div>
@* Value *@
<div class="sax-form-control ContactValue">
<div class="row">
<div class="col-sm-3">
<div class="sax-label">
<label for=""><span>@Localiser["label.value"]</span></label>
</div>
</div>
<div class="col-sm">
<div class="sax-control">
<input type="text" name="ContactValue" class="ContactValue sax-input" maxlength="200" />
</div>
<div class="sax-message"></div>
</div>
</div>
</div>
<div class="spacer"></div>
</div>
@@ -0,0 +1,118 @@
@* Controls - Item Address *@
@model SaxModel_Admin
@inject SaxLocaliser Localiser
@if (!Model.RegisterView(ViewContext)) return;
@* Item address - Line 1 *@
<div class="sax-form-control AddressLine1">
<div class="row">
<div class="col-sm-3">
<div class="sax-caption">
<label for=""><span>@Localiser["label.address"]</span></label>
</div>
</div>
<div class="col-sm">
<div class="sax-control row">
<div class="col"><input type="text" name="Address.Line1" class="AddressLine1 sax-input" autocomplete="off" maxlength="@Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.ItemAddress_Line1)" /> </div>
</div>
<div class="sax-message"></div>
</div>
</div>
</div>
@* Item address - Line 2 *@
<div class="sax-form-control AddressLine2">
<div class="row">
<div class="col-sm-3"></div>
<div class="col-sm">
<div class="sax-control row">
<div class="col"><input type="text" name="Address.Line2" class="AddressLine2 sax-input" autocomplete="off" maxlength="@Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.ItemAddress_Line2)" /> </div>
</div>
<div class="sax-message"></div>
</div>
</div>
</div>
@* Item address - Line 3 *@
<div class="sax-form-control AddressLine3">
<div class="row">
<div class="col-sm-3"></div>
<div class="col-sm">
<div class="sax-control row">
<div class="col"><input type="text" name="Address.Line3" class="AddressLine3 sax-input" autocomplete="off" maxlength="@Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.ItemAddress_Line3)" /> </div>
</div>
<div class="sax-message"></div>
</div>
</div>
</div>
@* Item address - Town *@
<div class="sax-form-control AddressTown">
<div class="row">
<div class="col-sm-3">
<div class="sax-label">
<label for=""><span>@Localiser["label.town"]</span><span class="required">&nbsp;(*)</span></label>
</div>
</div>
<div class="col-sm">
<div class="sax-control row">
<div class="col"><input type="text" name="Address.Town" class="AddressTown sax-input" autocomplete="off" maxlength="@Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.ItemAddress_Town)" /> </div>
</div>
<div class="sax-message"></div>
</div>
</div>
</div>
@* Item address - Province *@
<div class="sax-form-control AddressProvince">
<div class="row">
<div class="col-sm-3">
<div class="sax-label">
<label for=""><span>@Localiser["label.province"]</span></label>
</div>
</div>
<div class="col-sm">
<div class="sax-control row">
<div class="col"><input type="text" name="Address.Province" class="AddressProvince sax-input" autocomplete="off" maxlength="@Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.ItemAddress_Province)" /> </div>
</div>
<div class="sax-message"></div>
</div>
</div>
</div>
@* Item address - PostalCode *@
<div class="sax-form-control AddressPostalCode">
<div class="row">
<div class="col-sm-3">
<div class="sax-label">
<label for=""><span>@Localiser["label.postal.code"]</span></label>
</div>
</div>
<div class="col-sm">
<div class="sax-control row">
<div class="col"><input type="text" name="Address.PostalCode" class="AddressPostalCode sax-input" autocomplete="off" maxlength="@Model.GetControlMaxLength(SaxDbDfn_Core.ColumnIdx.ItemAddress_PostalCode)" /> </div>
</div>
<div class="sax-message"></div>
</div>
</div>
</div>
@* Address country *@
<div class="sax-form-control AddressRegionIdCountry">
<div class="row">
<div class="col-sm-3">
<div class="sax-label">
<label><span>@Localiser["label.country"]</span>&nbsp;<span class="required">*</span></label>
</div>
</div>
<div class="col-sm">
<div class="sax-control row">
<input type="hidden" name="Address.RegionIdCountry" class="AddressRegionIdCountry" />
<div class="col"><input type="text" class="AddressRegionNameCountry sax-input" readonly="readonly" /> </div>
<div class="fixed"><button class="sax-button AddressRegionCountrySelect" style="min-width:auto;margin-left:4px;padding:4px 6px">@Localiser["label.select"]</button></div>
</div>
<div class="sax-message"></div>
</div>
</div>
</div>
@@ -0,0 +1,83 @@
@* Contact Methods controls *@
@model SaxModel_Admin
@inject SaxLocaliser Localiser
@if (!Model.RegisterView(ViewContext)) return;
<style>
.ItemContactMethods .ContactMethods
{
min-height: 32px;
max-height: 64px;
background-color: #fff;
border: 1px solid #808080;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
overflow-y: scroll;
}
.ItemContactMethods .ContactMethods table.sax-data tr:hover
{
background-color: #f0f0f0;
}
.ItemContactMethods .ContactMethods table.sax-data .sax-label
{
margin: 0;
padding: 0 8px 0 0;
white-space: nowrap;
}
.ItemContactMethods .ContactMethods table.sax-data .sax-value
{
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
@* Contact Methods *@
<div class="sax-form-control ItemContactMethods">
<input type="hidden" name="ContactMethods" value="0" />
<div class="row">
<div class="col-sm-3 row">
<div class="col">
<div class="sax-label">
<label for=""><span>@Localiser["label.contact"]</span></label>
</div>
</div>
<div class="fixed">
@* Action to add new contact method. *@
<div class="sax-actions">
<div class="sax-action sax-add pr4"><img src="~/img/action/add24.png" alt="@(Localiser["label.add"])" title="@(Localiser["label.add"])" /></div>
</div>
</div>
</div>
<div class="col-sm">
<div class="sax-control row">
<div class="col">
@* List of existing contact methods with actions to edit/remove. *@
<div class="ContactMethods">
<table class="sax-data fullwidth"></table>
</div>
<table class="sax-panel-item-template">
<tr class="ContactMethod">
<td style="width:20px;"><div class="sax-label"></div></td>
<td style="width:70%;"><div class="sax-value"></div></td>
<td class="sax-actions right nowrap" style="width:10px;">
<input type="hidden" name="ContactMethod" />
<input type="hidden" name="ContactValue" />
<div class="nowrap">
<div class="inlineb sax-action sax-edit"><img src="~/img/action/edit16.png" alt="@(Localiser["label.edit"])" title="@(Localiser["label.edit"])" /></div>
<div class="inlineb sax-action sax-remove"><img src="~/img/action/delete16.png" alt="@(Localiser["label.remove"])" title="@(Localiser["label.remove"])" /></div>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sax-message"></div>
</div>
</div>
</div>
<partial name="~/Views/Common/ContactMethods.cshtml" />
@@ -0,0 +1,25 @@
@model SaxModel_Admin
@inject SaxLocaliser Localiser
@{
// Scripts.
Model.AdditionalFiles_JS.Add("js/sax-res-pag.js");
}
<script type="text/javascript">
sax.resultsPaging = sax.resultsPaging || { fn: {}, msg: {}, contexts: [] };
sax.resultsPaging.msg.pageInfo = '@Localiser.GetJavascriptText("label.page.info","{0}","{1}")';
</script>
@* Results paging *@
<div class="sax-results-paging hidden">
@Html.HiddenFor(model => model.Paging.PageNo, new { @id = "", @class = "PagingPageNo" })
@Html.HiddenFor(model => model.Paging.PageLength, new { @id = "", @class = "PagingPageLength" })
<div class="iblock sax-action sax-paging-first"><img src="~/img/action/first24.png" alt="@(Localiser["label.first"])" title="@(Localiser["label.first"])" /></div>
<div class="iblock sax-action sax-paging-prev"><img src="~/img/action/prev24.png" alt="@(Localiser["label.previous"])" title="@(Localiser["label.previous"])" /></div>
<div class="iblock sax-paging-info"></div>
<div class="iblock sax-action sax-paging-next"><img src="~/img/action/next24.png" alt="@(Localiser["label.next"])" title="@(Localiser["label.next"])" /></div>
<div class="iblock sax-action sax-paging-last"><img src="~/img/action/last24.png" alt="@(Localiser["label.last"])" title="@(Localiser["label.last"])" /></div>
</div>
@@ -0,0 +1,68 @@
@model SaxModel_Admin
@inject SaxLocaliser Localiser
@{
// Ensure view is only included once in page.
if (!Model.RegisterView(ViewContext)) return;
// Scripts.
Model.AdditionalFiles_JS.Add("js/sax-sel-cod.js");
}
<link rel="stylesheet" href="~/css/sax-selectCode.css?@Model.HrefVersion" />
<div id="dlgSelectCode" class="sax-dialog sax-form">
@* Filter *@
<div class="">
</div>
<div class="row h-100">
@* Available codes *@
<div class="col-sm-5">
<div class="h-100 d-flex flex-column">
<div class="">@Localiser["label.available"]:</div>
<div class="spacer"></div>
<div class="sax-selectCode-codes AvailableCodes flex-grow-1 mr-1"></div>
</div>
</div>
@* Code selection *@
<div class="col-sm-2">
<div class="spacer8"></div>
<div class="row">
<div class="col-sx-6 col-sm-12 center">
<button class="sax-button Select">@Localiser["label.select"]</button>
</div>
<div class="col-sx-6 col-sm-12 center">
<button class="sax-button Remove">@Localiser["label.remove"]</button>
</div>
</div>
</div>
@* Selected codes *@
<div class="col-sm-5">
<div class="h-100 d-flex flex-column">
<div class="">@Localiser["label.assigned"]:</div>
<div class="spacer"></div>
<div class="sax-selectCode-codes SelectedCodes flex-grow-1 ml-1"></div>
</div>
</div>
</div>
<div class="sax-panel-item-template">
<div class="sax-panel-item Code">
<div class="CodeDesc"></div>
</div>
</div>
</div>
@@ -0,0 +1,53 @@
@model SaxModel_Admin
@inject SaxLocaliser Localiser
@{
// Ensure view is only included once in page.
if (!Model.RegisterView(ViewContext)) return;
// Scripts.
Model.AdditionalFiles_JS.Add("js/sax-sel-itm.js");
}
<link rel="stylesheet" href="~/css/sax-selectItem.css?@Model.HrefVersion" />
<div id="dlgSelectItem" class="sax-dialog sax-form">
<div class="container-fluid h-100">
@* Selected Item *@
<div class="SelectedItem">
<div class="row">
<div class="col-sm-12"><div class="sax-label">@Localiser["label.item.selected"]:</div></div>
<div class="col-sm-12"><div class="SelectedItemName"></div></div>
</div>
<div class="spacer2"></div>
</div>
@* Item Filter *@
<div class="ItemFilter sax-form">
<div class="row">
<div class="col right"><div class="sax-label">@Localiser["label.search"]</div></div>
<div class="fixed"><input type="search" id="ifItemFilter" class="sax-input" autocomplete="off" onkeyup="sax.data.selectItem.fn.displayItems();" /></div>
</div>
<div class="spacer2"></div>
</div>
@* Available items *@
<div class="row">
<div class="col"><div class="sax-label bottom">@Localiser["label.items.available"]:</div></div>
</div>
<div class="row">
<div class="col-sm-12 sax-selectItem-items AvailableItems"></div>
</div>
</div>
<div class="sax-panel-item-template">
<div class="sax-panel-item Item">
<div class="ItemName"></div>
</div>
</div>
</div>
@@ -0,0 +1,53 @@
@model SaxModel_Admin
@inject SaxLocaliser Localiser
@{
// Ensure view is only included once in page.
if (!Model.RegisterView(ViewContext)) return;
// Scripts.
Model.AdditionalFiles_JS.Add("js/sax-sel-org.js");
}
<link rel="stylesheet" href="~/css/sax-selectOrganisation.css?@Model.HrefVersion" />
<script type="text/javascript">
sax.selectOrganisation = sax.selectOrganisation || { data: {}, fn: {}, $dlg: {} };
sax.selectOrganisation.dlgTitleSelectOrganisation = '@Localiser.GetJavascriptText("organisation.caption.select.organisation")';
</script>
<div id="dlgSelectOrganisation" class="sax-dialog sax-form">
@* Filter *@
@using (@Html.BeginForm(FormMethod.Post, new { @id = "frmSelectOrganisationFilter", @class = "sax-form", @onSubmit = "return false;" }))
{
<div class="sax-panel-filter">
<div class="sax-content">
<div class="row">
<div class="col-sm-5 sax-label text-left text-sm-right">@Localiser["label.search"]</div>
<div class="col-sm-7"><input type="search" class="sax-input FilterOrganisation" name="Filter.Organisation" autocomplete="off" maxlength="32" /></div>
</div>
<div class="row">
<div class="col center">
<partial name="~/Views/Common/ResultsPaging.cshtml" />
</div>
</div>
</div>
</div>
}
<div class="">
@* Organisations *@
<div class="sax-selection-results Organisations"></div>
</div>
<div class="sax-panel-item-template">
<div class="sax-panel-item Organisation">
<div class="Name"></div>
</div>
</div>
</div>
@@ -0,0 +1,10 @@
@model SaxModel_Admin
@inject SaxLocaliser Localiser
@* Icons. *@
<div class="sax-panel-item-template">
<img src="~/img/icon/tick-64.png" alt="" title="" class="sax-tick" style="max-width:16px;" />
<img src="~/img/icon/sphere-green.svg" alt="Active" title="Active" class="sax-status-atv" style="max-width:16px;" />
<img src="~/img/icon/sphere-orange.svg" alt="Inactive" title="Inactive" class="sax-status-ina" style="max-width:16px;" />
<img src="~/img/icon/sphere-purple.svg" alt="Deleted" title="Deleted" class="sax-status-del" style="max-width:16px;" />
</div>