Merge pull request #1099 from hargata/Hargata/1098

paginated cost table
This commit is contained in:
Hargata Softworks 2025-10-09 21:44:49 -06:00 committed by GitHub
commit a13cf0733b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 150 additions and 16 deletions

View File

@ -583,6 +583,24 @@ namespace CarCareTracker.Controllers
return PartialView("_LocaleSample", viewModel);
}
[Authorize(Roles = nameof(UserData.IsRootUser))]
public async Task<IActionResult> ImportOpenIDConfiguration(string configUrl)
{
if (!string.IsNullOrWhiteSpace(configUrl))
{
try
{
var httpClient = new HttpClient();
var openIdConfig = await httpClient.GetFromJsonAsync<OpenIDProviderConfig>(configUrl);
return Json(openIdConfig);
}
catch (Exception ex)
{
_logger.LogError($"Unable to retrieve OpenID Provider Config: {ex.Message}");
}
}
return Json(new OpenIDProviderConfig());
}
[Authorize(Roles = nameof(UserData.IsRootUser))]
[Route("/setup")]
public IActionResult Setup()
{

View File

@ -0,0 +1,14 @@
namespace CarCareTracker.Models
{
/// <summary>
/// Imported data from .well-known endpoint
/// </summary>
public class OpenIDProviderConfig
{
public string authorization_endpoint { get; set; }
public string token_endpoint { get; set; }
public string userinfo_endpoint { get; set; }
public string jwks_uri { get; set; }
public string end_session_endpoint { get; set; }
}
}

View File

@ -275,6 +275,11 @@
<!option value="false" @(Model.OIDCConfig.DisableRegularLogin ? "" : "selected")>@translator.Translate(userLanguage, "Disabled")</!option>
</select>
</div>
<div class="form-group">
<div class="d-flex align-items-center justify-content-center flex-column">
<div class="mt-2"><a class="btn btn-warning" onclick="importOpenIDConfig()">@translator.Translate(userLanguage, "Import OpenID Config")</a></div>
</div>
</div>
</form>
</div>
<div class="setup-wizard-content" data-page="5" style="display:none;">

View File

@ -11,7 +11,7 @@
}
@if (Model.CostData.Any())
{
<div>
<div class="monthlyCostBreakDownTable">
<div class="modal-header">
<h5 class="modal-title">@(translator.Translate(userLanguage, "Vehicle Monthly Cost Breakdown"))</h5>
<button type="button" class="btn-close" onclick="hideDataTable()" aria-label="Close"></button>
@ -25,7 +25,7 @@
<th scope="col" style="cursor:pointer;" onclick="toggleBarChartTableData()" class="col-2 flex-grow-1 flex-shrink-1 text-truncate">@(translator.Translate(userLanguage, "Year"))</th>
@foreach(int year in years){
if (year != default){
<th scope="col" style="cursor:pointer;" onclick="toggleBarChartTableData()" class="col-2 flex-grow-1 flex-shrink-1 text-truncate">@year</th>
<th scope="col" style="cursor:pointer;" onclick="toggleBarChartTableData()" class="col-3 flex-grow-1 flex-shrink-1 text-truncate">@year</th>
}
}
</tr>
@ -40,13 +40,13 @@
var dataToDisplay = Model.CostData.Where(x => x.Year == year && x.MonthName == month).FirstOrDefault();
if (dataToDisplay != null && dataToDisplay != default)
{
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate" report-data="cost">@(StaticHelper.HideZeroCost(dataToDisplay.Cost.ToString("C2"), hideZero))</td>
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate d-none" report-data="distance">@(dataToDisplay.DistanceTraveled != default ? $"{dataToDisplay.DistanceTraveled.ToString("N0")} {Model.DistanceUnit}" : "---")</td>
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate d-none" report-data="costperdistance">@(StaticHelper.HideZeroCost(dataToDisplay.CostPerDistanceTraveled.ToString("C2"), hideZero, $"/{Model.DistanceUnit}"))</td>
<td report-year="@year" class="col-3 flex-grow-1 flex-shrink-1 text-truncate" report-data="cost">@(StaticHelper.HideZeroCost(dataToDisplay.Cost.ToString("C2"), hideZero))</td>
<td report-year="@year" class="col-3 flex-grow-1 flex-shrink-1 text-truncate d-none" report-data="distance">@(dataToDisplay.DistanceTraveled != default ? $"{dataToDisplay.DistanceTraveled.ToString("N0")} {Model.DistanceUnit}" : "---")</td>
<td report-year="@year" class="col-3 flex-grow-1 flex-shrink-1 text-truncate d-none" report-data="costperdistance">@(StaticHelper.HideZeroCost(dataToDisplay.CostPerDistanceTraveled.ToString("C2"), hideZero, $"/{Model.DistanceUnit}"))</td>
} else {
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate" report-data="cost">@(StaticHelper.HideZeroCost(0M.ToString("C2"), hideZero))</td>
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate d-none" report-data="distance">@("---")</td>
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate d-none" report-data="costperdistance">@($"{StaticHelper.HideZeroCost(0M.ToString("C2"), hideZero, $"/{Model.DistanceUnit}")}")</td>
<td report-year="@year" class="col-3 flex-grow-1 flex-shrink-1 text-truncate" report-data="cost">@(StaticHelper.HideZeroCost(0M.ToString("C2"), hideZero))</td>
<td report-year="@year" class="col-3 flex-grow-1 flex-shrink-1 text-truncate d-none" report-data="distance">@("---")</td>
<td report-year="@year" class="col-3 flex-grow-1 flex-shrink-1 text-truncate d-none" report-data="costperdistance">@($"{StaticHelper.HideZeroCost(0M.ToString("C2"), hideZero, $"/{Model.DistanceUnit}")}")</td>
}
}
}
@ -67,15 +67,15 @@
{
var distanceTraveled = yearDataToDisplay.Sum(x => x.DistanceTraveled);
var costAccrued = yearDataToDisplay.Sum(x => x.Cost);
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate" report-data="cost">@(StaticHelper.HideZeroCost(costAccrued.ToString("C2"), hideZero))</td>
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate d-none" report-data="distance">@(distanceTraveled != default ? $"{distanceTraveled.ToString("N0")} {Model.DistanceUnit}" : "---")</td>
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate d-none" report-data="costperdistance">@(StaticHelper.HideZeroCost(distanceTraveled != default && costAccrued != default ? (costAccrued / distanceTraveled).ToString("C2") : 0M.ToString("C2"), hideZero, $"/{Model.DistanceUnit}"))</td>
<td report-year="@year" class="col-3 flex-grow-1 flex-shrink-1 text-truncate" report-data="cost">@(StaticHelper.HideZeroCost(costAccrued.ToString("C2"), hideZero))</td>
<td report-year="@year" class="col-3 flex-grow-1 flex-shrink-1 text-truncate d-none" report-data="distance">@(distanceTraveled != default ? $"{distanceTraveled.ToString("N0")} {Model.DistanceUnit}" : "---")</td>
<td report-year="@year" class="col-3 flex-grow-1 flex-shrink-1 text-truncate d-none" report-data="costperdistance">@(StaticHelper.HideZeroCost(distanceTraveled != default && costAccrued != default ? (costAccrued / distanceTraveled).ToString("C2") : 0M.ToString("C2"), hideZero, $"/{Model.DistanceUnit}"))</td>
}
else
{
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate" report-data="cost">@(StaticHelper.HideZeroCost(0M.ToString("C2"), hideZero))</td>
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate d-none" report-data="distance">@("---")</td>
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate d-none" report-data="costperdistance">@($"{StaticHelper.HideZeroCost(0M.ToString("C2"), hideZero)}")</td>
<td report-year="@year" class="col-3 flex-grow-1 flex-shrink-1 text-truncate" report-data="cost">@(StaticHelper.HideZeroCost(0M.ToString("C2"), hideZero))</td>
<td report-year="@year" class="col-3 flex-grow-1 flex-shrink-1 text-truncate d-none" report-data="distance">@("---")</td>
<td report-year="@year" class="col-3 flex-grow-1 flex-shrink-1 text-truncate d-none" report-data="costperdistance">@($"{StaticHelper.HideZeroCost(0M.ToString("C2"), hideZero)}")</td>
}
}
}
@ -86,6 +86,14 @@
</div>
</div>
</div>
@if(years.Count() > 5)
{
<div class="modal-footer">
<button type="button" class="btn btn-secondary btn-costdata-prev"><i class="bi bi-caret-left-fill"></i></button>
<button type="button" class="btn btn-secondary btn-costdata-next"><i class="bi bi-caret-right-fill"></i></button>
</div>
<script>getCostDataTablePage(1)</script>
}
</div>
}
else

File diff suppressed because one or more lines are too long

View File

@ -429,3 +429,50 @@ function showReportAdvancedParameters() {
$(".report-advanced-parameters").addClass("d-none");
}
}
function getCostDataTablePage(pageNumber) {
let years = $(".monthlyCostBreakDownTable").find("th").map((index, elem) => $(elem).text()).toArray().filter(y => !isNaN(y));
if (years.length < 5) {
return;
}
let firstYear = years[0];
let lastYear = years[years.length - 1];
let pageSize = 5;
// Calculate the starting index for the current page.
// Page numbers are usually 1-based, so we subtract 1 for 0-based array indexing.
let startIndex = (pageNumber - 1) * pageSize;
// Calculate the ending index for the current page.
// slice() extracts up to (but not including) the end index.
let endIndex = startIndex + pageSize;
// Use slice() to extract the elements for the current page.
let yearsToDisplay = years.slice(startIndex, endIndex);
$(".monthlyCostBreakDownTable").find("th").map((index, elem) => {
let year = $(elem).text();
if (yearsToDisplay.includes(year)) {
$(elem).show();
$(".monthlyCostBreakDownTable").find(`tr > td[report-year='${year}']`).show();
} else if (index != 0) {
$(elem).hide();
$(".monthlyCostBreakDownTable").find(`tr > td[report-year='${year}']`).hide();
}
});
let leftNavButton = $('.monthlyCostBreakDownTable').find('.btn-costdata-prev');
let rightNavButton = $('.monthlyCostBreakDownTable').find('.btn-costdata-next');
leftNavButton.off('click');
rightNavButton.off('click');
if (yearsToDisplay.includes(firstYear)) {
leftNavButton.attr('disabled', true);
} else {
let prevPage = pageNumber - 1
leftNavButton.on('click', function () { getCostDataTablePage(prevPage); });
leftNavButton.attr('disabled', false);
}
if (yearsToDisplay.includes(lastYear)) {
rightNavButton.attr('disabled', true);
} else {
let nextPage = pageNumber + 1
rightNavButton.on('click', function () { getCostDataTablePage(nextPage); });
rightNavButton.attr('disabled', false);
}
}

View File

@ -192,6 +192,48 @@ function sendTestEmail() {
}
});
}
function importOpenIDConfig() {
Swal.fire({
title: 'Import OpenID Config',
html: `
<input type="text" id="openIdImportEndpoint" class="swal2-input" placeholder=".well-known endpoint" onkeydown="handleSwalEnter(event)">
`,
confirmButtonText: 'Import',
focusConfirm: false,
preConfirm: () => {
const importEndpoint = $("#openIdImportEndpoint").val();
if (!importEndpoint || importEndpoint.trim() == '') {
Swal.showValidationMessage(`Please enter a valid URL`);
}
return { importEndpoint }
},
}).then(function (result) {
if (result.isConfirmed) {
$.post('/Home/ImportOpenIDConfiguration', { configUrl: result.value.importEndpoint }, function (data) {
if (data != null && data != undefined) {
if (data.authorization_endpoint != null) {
$('#inputOIDCAuth').val(data.authorization_endpoint);
}
if (data.token_endpoint != null) {
$('#inputOIDCToken').val(data.token_endpoint);
}
if (data.userinfo_endpoint != null) {
$('#inputOIDCUserInfo').val(data.userinfo_endpoint);
}
if (data.jwks_uri != null) {
$('#inputOIDCJwks').val(data.jwks_uri);
}
if (data.end_session_endpoint != null) {
$('#inputOIDCLogout').val(data.end_session_endpoint);
}
}
else {
errorToast(genericErrorMessage());
}
});
}
});
}
function nextOnSkip(sender) {
if ($(sender).is(":checked")) {
nextSetupPage();