Compare commits

...

8 Commits

Author SHA1 Message Date
Hargata Softworks
2ab822af8f
Merge pull request #1126 from hargata/Hargata/fix.reminder.bug
fix UI bug when creating reminder record from other records.
2025-11-05 12:37:32 -07:00
DESKTOP-T0O5CDB\DESK-555BD
0cef3c9044 fix UI bug when creating reminder record from other records. 2025-11-05 12:36:33 -07:00
Hargata Softworks
9ef2124c8a
Merge pull request #1125 from hargata/Hargata/1119
add GET API Endpoint to return all extrafields
2025-11-05 11:06:17 -07:00
DESKTOP-T0O5CDB\DESK-555BD
fd9d57c3db add documentation 2025-11-05 11:04:35 -07:00
DESKTOP-T0O5CDB\DESK-555BD
a3e0da1ebb add GET API Endpoint to return all extrafields 2025-11-05 10:33:48 -07:00
Hargata Softworks
ebca12d82a
Merge pull request #1124 from hargata/Hargata/add.api
API Enhancements
2025-11-05 09:11:21 -07:00
DESKTOP-T0O5CDB\DESK-555BD
9b00933aa8 fix action item progress 2025-11-05 09:10:41 -07:00
DESKTOP-T0O5CDB\DESK-555BD
98ef7d455b Add check to ensure that reminder for inspection still exists. Add recordId to response object returned from API 2025-11-05 07:52:43 -07:00
12 changed files with 173 additions and 20 deletions

View File

@ -28,6 +28,7 @@ namespace CarCareTracker.Controllers
private readonly IInspectionRecordTemplateDataAccess _inspectionRecordTemplateDataAccess;
private readonly IUserAccessDataAccess _userAccessDataAccess;
private readonly IUserRecordDataAccess _userRecordDataAccess;
private readonly IExtraFieldDataAccess _extraFieldDataAccess;
private readonly IReminderHelper _reminderHelper;
private readonly IGasHelper _gasHelper;
private readonly IUserLogic _userLogic;
@ -55,6 +56,7 @@ namespace CarCareTracker.Controllers
IInspectionRecordTemplateDataAccess inspectionRecordTemplateDataAccess,
IUserAccessDataAccess userAccessDataAccess,
IUserRecordDataAccess userRecordDataAccess,
IExtraFieldDataAccess extraFieldDataAccess,
IMailHelper mailHelper,
IFileHelper fileHelper,
IConfigHelper config,
@ -79,6 +81,7 @@ namespace CarCareTracker.Controllers
_inspectionRecordTemplateDataAccess = inspectionRecordTemplateDataAccess;
_userAccessDataAccess = userAccessDataAccess;
_userRecordDataAccess = userRecordDataAccess;
_extraFieldDataAccess = extraFieldDataAccess;
_mailHelper = mailHelper;
_gasHelper = gasHelper;
_reminderHelper = reminderHelper;
@ -329,7 +332,7 @@ namespace CarCareTracker.Controllers
};
_planRecordDataAccess.SavePlanRecordToVehicle(planRecord);
StaticHelper.NotifyAsync(_config.GetWebHookUrl(), WebHookPayload.FromPlanRecord(planRecord, "planrecord.add.api", User.Identity.Name));
return Json(OperationResponse.Succeed("Plan Record Added"));
return Json(OperationResponse.Succeed("Plan Record Added", new { recordId = planRecord.Id }));
}
catch (Exception ex)
{
@ -545,7 +548,7 @@ namespace CarCareTracker.Controllers
_odometerLogic.AutoInsertOdometerRecord(odometerRecord);
}
StaticHelper.NotifyAsync(_config.GetWebHookUrl(), WebHookPayload.FromGenericRecord(serviceRecord, "servicerecord.add.api", User.Identity.Name));
return Json(OperationResponse.Succeed("Service Record Added"));
return Json(OperationResponse.Succeed("Service Record Added", new { recordId = serviceRecord.Id }));
}
catch (Exception ex)
{
@ -742,7 +745,7 @@ namespace CarCareTracker.Controllers
}
StaticHelper.NotifyAsync(_config.GetWebHookUrl(), WebHookPayload.FromGenericRecord(repairRecord, "repairrecord.add.api", User.Identity.Name));
return Json(OperationResponse.Succeed("Repair Record Added"));
return Json(OperationResponse.Succeed("Repair Record Added", new { recordId = repairRecord.Id }));
}
catch (Exception ex)
{
@ -939,7 +942,7 @@ namespace CarCareTracker.Controllers
_odometerLogic.AutoInsertOdometerRecord(odometerRecord);
}
StaticHelper.NotifyAsync(_config.GetWebHookUrl(), WebHookPayload.FromGenericRecord(upgradeRecord, "upgraderecord.add.api", User.Identity.Name));
return Json(OperationResponse.Succeed("Upgrade Record Added"));
return Json(OperationResponse.Succeed("Upgrade Record Added", new { recordId = upgradeRecord.Id }));
}
catch (Exception ex)
{
@ -1158,7 +1161,7 @@ namespace CarCareTracker.Controllers
_taxRecordDataAccess.SaveTaxRecordToVehicle(taxRecord);
_vehicleLogic.UpdateRecurringTaxes(vehicleId);
StaticHelper.NotifyAsync(_config.GetWebHookUrl(), WebHookPayload.FromTaxRecord(taxRecord, "taxrecord.add.api", User.Identity.Name));
return Json(OperationResponse.Succeed("Tax Record Added"));
return Json(OperationResponse.Succeed("Tax Record Added", new { recordId = taxRecord.Id }));
}
catch (Exception ex)
{
@ -1352,7 +1355,7 @@ namespace CarCareTracker.Controllers
};
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(odometerRecord);
StaticHelper.NotifyAsync(_config.GetWebHookUrl(), WebHookPayload.FromOdometerRecord(odometerRecord, "odometerrecord.add.api", User.Identity.Name));
return Json(OperationResponse.Succeed("Odometer Record Added"));
return Json(OperationResponse.Succeed("Odometer Record Added", new { recordId = odometerRecord.Id }));
} catch (Exception ex)
{
Response.StatusCode = 500;
@ -1561,7 +1564,7 @@ namespace CarCareTracker.Controllers
_odometerLogic.AutoInsertOdometerRecord(odometerRecord);
}
StaticHelper.NotifyAsync(_config.GetWebHookUrl(), WebHookPayload.FromGasRecord(gasRecord, "gasrecord.add.api", User.Identity.Name));
return Json(OperationResponse.Succeed("Gas Record Added"));
return Json(OperationResponse.Succeed("Gas Record Added", new { recordId = gasRecord.Id }));
}
catch (Exception ex)
{
@ -1759,7 +1762,7 @@ namespace CarCareTracker.Controllers
};
_reminderRecordDataAccess.SaveReminderRecordToVehicle(reminderRecord);
StaticHelper.NotifyAsync(_config.GetWebHookUrl(), WebHookPayload.FromReminderRecord(reminderRecord, "reminderrecord.add.api", User.Identity.Name));
return Json(OperationResponse.Succeed("Reminder Record Added"));
return Json(OperationResponse.Succeed("Reminder Record Added", new { recordId = reminderRecord.Id }));
}
catch (Exception ex)
{
@ -1980,6 +1983,42 @@ namespace CarCareTracker.Controllers
return Json(OperationResponse.Succeed($"Emails Sent({operationResponses.Count(x => x.Success)}), Emails Failed({operationResponses.Count(x => !x.Success)}), Check Recipient Settings"));
}
}
[HttpGet]
[Route("/api/extrafields")]
public IActionResult GetExtraFields()
{
try
{
List<RecordExtraFieldExportModel> result = new List<RecordExtraFieldExportModel>();
var extraFields = _extraFieldDataAccess.GetExtraFields();
if (extraFields.Any())
{
foreach(RecordExtraField extraField in extraFields)
{
if (extraField.ExtraFields.Any())
{
result.Add(new RecordExtraFieldExportModel
{
RecordType = ((ImportMode)extraField.Id).ToString(),
ExtraFields = extraField.ExtraFields.Select(x => new ExtraFieldExportModel { Name = x.Name, IsRequired = x.IsRequired.ToString(), FieldType = x.FieldType.ToString() }).ToList()
});
}
}
}
if (_config.GetInvariantApi() || Request.Headers.ContainsKey("culture-invariant"))
{
return Json(result, StaticHelper.GetInvariantOption());
}
else
{
return Json(result);
}
} catch (Exception ex)
{
Response.StatusCode = 500;
return Json(OperationResponse.Failed(ex.Message));
}
}
[Authorize(Roles = nameof(UserData.IsRootUser))]
[HttpGet]
[Route("/api/makebackup")]

View File

@ -38,13 +38,32 @@ namespace CarCareTracker.Controllers
[HttpGet]
public IActionResult GetEditInspectionRecordTemplatePartialView(int inspectionRecordTemplateId)
{
var result = _inspectionRecordTemplateDataAccess.GetInspectionRecordTemplateById(inspectionRecordTemplateId);
var existingRecord = _inspectionRecordTemplateDataAccess.GetInspectionRecordTemplateById(inspectionRecordTemplateId);
//security check.
if (!_userLogic.UserCanEditVehicle(GetUserID(), result.VehicleId))
if (!_userLogic.UserCanEditVehicle(GetUserID(), existingRecord.VehicleId))
{
return Redirect("/Error/Unauthorized");
}
return PartialView("Inspection/_InspectionRecordTemplateEditModal", result);
if (existingRecord.ReminderRecordId.Any())
{
bool reminderMissing = false;
//check if reminder still exists and is still recurring.
foreach (int reminderRecordId in existingRecord.ReminderRecordId)
{
var existingReminder = _reminderRecordDataAccess.GetReminderRecordById(reminderRecordId);
if (existingReminder is null || existingReminder.Id == default || !existingReminder.IsRecurring)
{
reminderMissing = true;
break;
}
}
if (reminderMissing)
{
//clear out reminders attached to record.
existingRecord.ReminderRecordId = new List<int>();
}
}
return PartialView("Inspection/_InspectionRecordTemplateEditModal", existingRecord);
}
[HttpGet]
public IActionResult GetAddInspectionRecordFieldPartialView()
@ -111,15 +130,34 @@ namespace CarCareTracker.Controllers
[HttpGet]
public IActionResult GetAddInspectionRecordPartialView(int inspectionRecordTemplateId)
{
var result = _inspectionRecordTemplateDataAccess.GetInspectionRecordTemplateById(inspectionRecordTemplateId);
var existingRecord = _inspectionRecordTemplateDataAccess.GetInspectionRecordTemplateById(inspectionRecordTemplateId);
//security check.
if (!_userLogic.UserCanEditVehicle(GetUserID(), result.VehicleId))
if (!_userLogic.UserCanEditVehicle(GetUserID(), existingRecord.VehicleId))
{
return Redirect("/Error/Unauthorized");
}
//populate date
result.Date = DateTime.Now.ToShortDateString();
return PartialView("Inspection/_InspectionRecordModal", result);
existingRecord.Date = DateTime.Now.ToShortDateString();
if (existingRecord.ReminderRecordId.Any())
{
bool reminderMissing = false;
//check if reminder still exists and is still recurring.
foreach (int reminderRecordId in existingRecord.ReminderRecordId)
{
var existingReminder = _reminderRecordDataAccess.GetReminderRecordById(reminderRecordId);
if (existingReminder is null || existingReminder.Id == default || !existingReminder.IsRecurring)
{
reminderMissing = true;
break;
}
}
if (reminderMissing)
{
//clear out reminders attached to record.
existingRecord.ReminderRecordId = new List<int>();
}
}
return PartialView("Inspection/_InspectionRecordModal", existingRecord);
}
[HttpGet]
public IActionResult GetViewInspectionRecordPartialView(int inspectionRecordId)
@ -200,6 +238,7 @@ namespace CarCareTracker.Controllers
Description = inspectionField.ActionItemDescription,
ImportMode = inspectionField.ActionItemType,
Priority = inspectionField.ActionItemPriority,
Progress = PlanProgress.Backlog,
Notes = $"Auto Insert From Inspection Record: {inspectionRecord.Description}",
Files = StaticHelper.CreateAttachmentFromRecord(ImportMode.InspectionRecord, convertedRecord.Id, convertedRecord.Description)
});

View File

@ -12,6 +12,12 @@ namespace CarCareTracker.External.Implementations
{
_liteDB = liteDB;
}
public List<RecordExtraField> GetExtraFields()
{
var db = _liteDB.GetLiteDB();
var table = db.GetCollection<RecordExtraField>(tableName);
return table.FindAll().ToList();
}
public RecordExtraField GetExtraFieldsById(int importMode)
{
var db = _liteDB.GetLiteDB();

View File

@ -29,6 +29,29 @@ namespace CarCareTracker.External.Implementations
_logger.LogError(ex.Message);
}
}
public List<RecordExtraField> GetExtraFields()
{
try
{
string cmd = $"SELECT data FROM app.{tableName}";
var results = new List<RecordExtraField>();
using (var ctext = pgDataSource.CreateCommand(cmd))
{
using (NpgsqlDataReader reader = ctext.ExecuteReader())
while (reader.Read())
{
RecordExtraField result = JsonSerializer.Deserialize<RecordExtraField>(reader["data"] as string);
results.Add(result);
}
}
return results;
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
return new List<RecordExtraField>();
}
}
public RecordExtraField GetExtraFieldsById(int importMode)
{
try

View File

@ -4,6 +4,7 @@ namespace CarCareTracker.External.Interfaces
{
public interface IExtraFieldDataAccess
{
public List<RecordExtraField> GetExtraFields();
public RecordExtraField GetExtraFieldsById(int importMode);
public bool SaveExtraFields(RecordExtraField record);
}

View File

@ -1,4 +1,5 @@
using CarCareTracker.Helper;
using System.Text.Json.Serialization;
namespace CarCareTracker.Models
{
@ -6,6 +7,8 @@ namespace CarCareTracker.Models
{
public bool Success { get; set; }
public string Message { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public object? AdditionalData { get; set; }
}
public class OperationResponse: OperationResponseBase
{
@ -13,6 +16,10 @@ namespace CarCareTracker.Models
{
return new OperationResponse { Success = true, Message = message};
}
public static OperationResponse Succeed(string message, object additionalData)
{
return new OperationResponse { Success = true, Message = message, AdditionalData = additionalData };
}
public static OperationResponse Failed(string message = "")
{
if (string.IsNullOrWhiteSpace(message))

View File

@ -42,5 +42,6 @@
Tags = Tags
};
}
public bool CreatedFromRecord { get; set; } = false;
}
}

View File

@ -183,4 +183,16 @@ namespace CarCareTracker.Models
public string Name { get; set; }
public string Location { get; set; }
}
public class RecordExtraFieldExportModel
{
public string RecordType { get; set; }
public List<ExtraFieldExportModel> ExtraFields { get; set; }
}
public class ExtraFieldExportModel
{
public string Name { get; set; }
[JsonConverter(typeof(FromBoolOptional))]
public string IsRequired { get; set; }
public string FieldType { get; set; }
}
}

View File

@ -762,6 +762,20 @@
No Params
</div>
</div>
<div class="row api-method">
<div class="col-1">
<span class="badge bg-success">GET</span>
</div>
<div class="col-5 copyable testable">
<code>/api/extrafields</code>
</div>
<div class="col-3">
Returns extra fields configured
</div>
<div class="col-3">
No Params
</div>
</div>
<div class="row api-method">
<div class="col-1">
<span class="badge bg-primary">POST</span>
@ -792,7 +806,8 @@
</div>
<div class="col-3">
(must be root user)<br />
urgencies[]=[NotUrgent,Urgent,VeryUrgent,PastDue](optional)
urgencies[]=[NotUrgent,Urgent,VeryUrgent,PastDue](optional)<br />
tags - tags separated by space(optional)
</div>
</div>
<div class="row api-method">

View File

@ -144,6 +144,11 @@
var customMonthInterval = @Model.CustomMonthInterval;
var customMonthIntervalUnit = decodeHTMLEntities('@Model.CustomMonthIntervalUnit');
function getReminderRecordModelData() {
return { id: @Model.Id, mileageInterval: decodeHTMLEntities('@Model.ReminderMileageInterval.ToString()'), monthInterval: decodeHTMLEntities('@Model.ReminderMonthInterval.ToString()')}
return {
id: @Model.Id,
createdFromRecord: @Model.CreatedFromRecord.ToString().ToLower(),
mileageInterval: decodeHTMLEntities('@Model.ReminderMileageInterval.ToString()'),
monthInterval: decodeHTMLEntities('@Model.ReminderMonthInterval.ToString()')
}
}
</script>

View File

@ -129,8 +129,12 @@ function saveReminderRecordToVehicle(isEdit) {
if (data) {
successToast(isEdit ? "Reminder Updated" : "Reminder Added.");
hideAddReminderRecordModal();
if (!getReminderRecordModelData().createdFromRecord) {
saveScrollPosition();
getVehicleReminders(formValues.vehicleId);
} else {
getVehicleHaveImportantReminders(formValues.vehicleId);
}
} else {
errorToast(genericErrorMessage());
}

View File

@ -228,6 +228,7 @@ function deleteVehicle(vehicleId) {
}
function showAddReminderModal(reminderModalInput) {
if (reminderModalInput != undefined) {
reminderModalInput['createdFromRecord'] = true;
$.post('/Vehicle/GetAddReminderRecordPartialView', { reminderModel: reminderModalInput }, function (data) {
$("#reminderRecordModalContent").html(data);
initDatePicker($('#reminderDate'), true);