added delete function and backend for managing collaborators

This commit is contained in:
DESKTOP-T0O5CDB\DESK-555BD 2025-09-26 17:04:24 -06:00
parent 59b58a836c
commit 18f086de32
5 changed files with 171 additions and 10 deletions

View File

@ -170,6 +170,69 @@ namespace CarCareTracker.Controllers
return Json(result);
}
[HttpPost]
public IActionResult DeleteVehicles(List<int> vehicleIds)
{
List<bool> results = new List<bool>();
foreach(int vehicleId in vehicleIds)
{
if (_userLogic.UserCanEditVehicle(GetUserID(), vehicleId))
{
//Delete all service records, gas records, notes, etc.
var result = _gasRecordDataAccess.DeleteAllGasRecordsByVehicleId(vehicleId) &&
_serviceRecordDataAccess.DeleteAllServiceRecordsByVehicleId(vehicleId) &&
_collisionRecordDataAccess.DeleteAllCollisionRecordsByVehicleId(vehicleId) &&
_taxRecordDataAccess.DeleteAllTaxRecordsByVehicleId(vehicleId) &&
_noteDataAccess.DeleteAllNotesByVehicleId(vehicleId) &&
_reminderRecordDataAccess.DeleteAllReminderRecordsByVehicleId(vehicleId) &&
_upgradeRecordDataAccess.DeleteAllUpgradeRecordsByVehicleId(vehicleId) &&
_planRecordDataAccess.DeleteAllPlanRecordsByVehicleId(vehicleId) &&
_planRecordTemplateDataAccess.DeleteAllPlanRecordTemplatesByVehicleId(vehicleId) &&
_supplyRecordDataAccess.DeleteAllSupplyRecordsByVehicleId(vehicleId) &&
_odometerRecordDataAccess.DeleteAllOdometerRecordsByVehicleId(vehicleId) &&
_userLogic.DeleteAllAccessToVehicle(vehicleId) &&
_dataAccess.DeleteVehicle(vehicleId);
if (result)
{
StaticHelper.NotifyAsync(_config.GetWebHookUrl(), WebHookPayload.Generic(string.Empty, "vehicle.delete", User.Identity.Name, vehicleId.ToString()));
}
results.Add(result);
}
}
return Json(results.All(x => x));
}
[HttpPost]
public IActionResult GetVehiclesCollaborators(List<int> vehicleIds)
{
List<UserCollaborator> allCollaborators = new List<UserCollaborator>();
if (vehicleIds.Count() == 1)
{
//only one vehicle to manage
if (_userLogic.UserCanEditVehicle(GetUserID(), vehicleIds.First()))
{
var vehicleCollaborators = _userLogic.GetCollaboratorsForVehicle(vehicleIds.First());
return Json(vehicleCollaborators);
} else
{
return Json(new List<UserCollaborator>());
}
}
else
{
foreach(int vehicleId in vehicleIds)
{
if (_userLogic.UserCanEditVehicle(GetUserID(), vehicleId))
{
var vehicleCollaborators = _userLogic.GetCollaboratorsForVehicle(vehicleId);
allCollaborators.AddRange(vehicleCollaborators);
}
}
var groupedCollaborations = allCollaborators.GroupBy(x => x.UserName);
var commonCollaborators = groupedCollaborations.Where(x => x.Count() == vehicleIds.Count()).Select(y => y.Key);
var partialCollaborators = groupedCollaborations.Where(x => x.Count() != vehicleIds.Count()).Select(y => y.Key);
return Json(allCollaborators);
}
}
[HttpPost]
public IActionResult DuplicateVehicleCollaborators(int sourceVehicleId, int destVehicleId)
{
try

View File

@ -168,12 +168,15 @@
<div class="stickerPrintContainer hideOnPrint">
</div>
<ul class="garage-context-menu dropdown-menu" style="display:none;">
<li><a class="dropdown-item" href="#" onclick=""><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Edit")</span><i class="bi bi-pencil-square"></i></div></a></li>
<li><a class="dropdown-item" href="#" onclick=""><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Manage Collaborators")</span><i class="bi bi-people"></i></div></a></li>
<li><a class="context-menu-multiple context-menu-select-all dropdown-item" href="#" onclick="selectAllVehicles()"><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Select All")</span><i class="bi bi-check-square"></i></div></a></li>
<li><a class="context-menu-multiple context-menu-deselect-all dropdown-item" href="#" onclick="clearSelectedVehicles()"><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Deselect All")</span><i class="bi bi-x-square"></i></div></a></li>
<li><hr class="context-menu-multiple dropdown-divider"></li>
<li><a class="context-menu-active-single dropdown-item" href="#" onclick=""><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Edit")</span><i class="bi bi-pencil-square"></i></div></a></li>
<li><a class="dropdown-item" href="#" onclick="manageCollaborators(selectedVehicles)"><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Manage Collaborators")</span><i class="bi bi-people"></i></div></a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="sortGarage()"><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Sort")</span><i class="garage-sort-icon bi bi-arrow-down-up"></i></div></a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item text-danger" href="#" onclick=""><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Delete")</span><i class="bi bi-trash"></i></div></a></li>
<li><a class="dropdown-item text-danger" href="#" onclick="deleteVehicles(selectedVehicles)"><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Delete")</span><i class="bi bi-trash"></i></div></a></li>
</ul>
<script>
bindWindowResize();

View File

@ -45,8 +45,8 @@
{
@if (!(userConfig.HideSoldVehicles && !string.IsNullOrWhiteSpace(vehicle.SoldDate)))
{
<div class="col-xl-2 col-lg-3 col-md-4 col-sm-4 col-6 garage-item" oncontextmenu="showGarageContextMenu(this)" data-tags='@string.Join(" ", vehicle.Tags)' data-rowId="@vehicle.Id" id="gridVehicle_@vehicle.Id" data-bs-toggle="tooltip" data-bs-html="true" data-bs-title="@await Html.PartialAsync("_VehicleExtraFields", vehicle.ExtraFields)" data-bs-placement="bottom" data-bs-trigger="manual" onmouseenter="loadPinnedNotes(@vehicle.Id)" ontouchstart="loadPinnedNotes(@vehicle.Id)" ontouchcancel="hidePinnedNotes(@vehicle.Id)" ontouchend="hidePinnedNotes(@vehicle.Id)" onmouseleave="hidePinnedNotes(@vehicle.Id)" onmousemove="garageRangeMouseMove(this)">
<div class="card" onclick="viewVehicle(@vehicle.Id)">
<div class="col-xl-2 col-lg-3 col-md-4 col-sm-4 col-6 garage-item" onclick="handleGarageItemClick(this, @vehicle.Id)" oncontextmenu="showGarageContextMenu(this)" data-tags='@string.Join(" ", vehicle.Tags)' data-rowId="@vehicle.Id" id="gridVehicle_@vehicle.Id" data-bs-toggle="tooltip" data-bs-html="true" data-bs-title="@await Html.PartialAsync("_VehicleExtraFields", vehicle.ExtraFields)" data-bs-placement="bottom" data-bs-trigger="manual" onmouseenter="loadPinnedNotes(@vehicle.Id)" ontouchstart="loadPinnedNotes(@vehicle.Id)" ontouchcancel="hidePinnedNotes(@vehicle.Id)" ontouchend="hidePinnedNotes(@vehicle.Id)" onmouseleave="hidePinnedNotes(@vehicle.Id)" onmousemove="garageRangeMouseMove(this)">
<div class="card">
<img src="@vehicle.ImageLocation" style="height:145px; object-fit:scale-down; pointer-events:none; @(string.IsNullOrWhiteSpace(vehicle.SoldDate) ? "" : "filter: grayscale(100%);")" />
@if (!string.IsNullOrWhiteSpace(vehicle.SoldDate))
{

View File

@ -275,14 +275,43 @@ function showGarageContextMenu(e) {
}
$(".garage-context-menu").fadeIn("fast");
$(".garage-context-menu").css({
left: getMenuPosition(event.clientX, 'width', 'scrollLeft'),
top: getMenuPosition(event.clientY, 'height', 'scrollTop')
left: getGarageMenuPosition(event.clientX, 'width', 'scrollLeft'),
top: getGarageMenuPosition(event.clientY, 'height', 'scrollTop')
});
if (!$(e).hasClass('garage-active')) {
clearSelectedVehicles();
addToSelectedVehicles($(e).attr('data-rowId'));
$(e).addClass('garage-active');
}
determineGarageContextMenu();
}
function determineGarageContextMenu() {
let garageItems = $('.garage-item:visible');
let garageItemsActive = $('.garage-item.garage-active:visible');
if (garageItemsActive.length == 1) {
$(".context-menu-active-single").show();
$(".context-menu-active-multiple").hide();
} else if (garageItemsActive.length > 1) {
$(".context-menu-active-single").hide();
$(".context-menu-active-multiple").show();
} else {
$(".context-menu-active-single").hide();
$(".context-menu-active-multiple").hide();
}
if (garageItems.length > 1) {
$(".context-menu-multiple").show();
if (garageItems.length == garageItemsActive.length) {
//all rows are selected, show deselect all button.
$(".context-menu-deselect-all").show();
$(".context-menu-select-all").hide();
} else if (garageItems.length != garageItemsActive.length) {
//not all rows are selected, show select all button.
$(".context-menu-select-all").show();
$(".context-menu-deselect-all").hide();
}
} else {
$(".context-menu-multiple").hide();
}
}
function garageRangeMouseMove(e) {
if (isDragging) {
@ -292,7 +321,54 @@ function garageRangeMouseMove(e) {
}
}
}
function removeFromSelectedVehicles(id) {
var rowIndex = selectedVehicles.findIndex(x => x == id)
if (rowIndex != -1) {
selectedVehicles.splice(rowIndex, 1);
}
}
function handleGarageItemClick(e, vehicleId) {
if (!(event.ctrlKey || event.metaKey)) {
viewVehicle(vehicleId);
} else if (!$(e).hasClass('garage-active')) {
addToSelectedVehicles($(e).attr('data-rowId'));
$(e).addClass('garage-active');
} else if ($(e).hasClass('garage-active')) {
removeFromSelectedVehicles($(e).attr('data-rowId'));
$(e).removeClass('garage-active');
}
}
function deleteVehicles(vehicleIds) {
if (vehicleIds.length == 0) {
return;
}
let messageWording = vehicleIds.length > 1 ? `these ${vehicleIds.length} vehicles` : 'this vehicle';
Swal.fire({
title: "Confirm Deletion?",
text: `This will also delete all data tied to ${messageWording}. Deleted Vehicles and their associated data cannot be restored.`,
showCancelButton: true,
confirmButtonText: "Delete",
confirmButtonColor: "#dc3545"
}).then((result) => {
if (result.isConfirmed) {
$.post('/Vehicle/DeleteVehicles', { vehicleIds: vehicleIds }, function (data) {
if (data) {
loadGarage();
}
})
}
});
}
function manageCollaborators(vehicleIds) {
if (vehicleIds.length == 0) {
return;
}
$.post('/Vehicle/GetVehiclesCollaborators', { vehicleIds: vehicleIds }, function (data) {
if (data) {
console.log(data);
}
})
}
// end context menu
function sortGarage() {
//check current sort state

View File

@ -1159,6 +1159,7 @@ $(window).on('keydown', function (e) {
e.preventDefault();
e.stopPropagation();
selectAllRows();
selectAllVehicles();
}
}
});
@ -1172,11 +1173,18 @@ function selectAllRows() {
addToSelectedRows($(elem).attr('data-rowId'));
});
}
function selectAllVehicles() {
clearSelectedVehicles();
$('.garage-item:visible').addClass('garage-active');
$('.garage-item:visible').map((index, elem) => {
addToSelectedVehicles($(elem).attr('data-rowId'));
});
}
function rangeMouseDown(e) {
if (isRightClick(e)) {
return;
}
var contextMenuAction = $(e.target).parents(".table-context-menu > li > .dropdown-item").length > 0 || $(e.target).is(".table-context-menu > li > .dropdown-item");
var contextMenuAction = $(e.target).parents(".table-context-menu > li > .dropdown-item").length > 0 || $(e.target).is(".table-context-menu > li > .dropdown-item") || $(e.target).parents(".garage-context-menu > li > .dropdown-item").length > 0 || $(e.target).is(".garage-context-menu > li > .dropdown-item");
var selectMode = $("#chkSelectMode").length > 0 ? $("#chkSelectMode").is(":checked") : false;
if (!(e.ctrlKey || e.metaKey || selectMode) && !contextMenuAction) {
clearSelectedRows();
@ -1255,7 +1263,6 @@ function showTableContextMenu(e) {
return;
}
$(".table-context-menu").fadeIn("fast");
determineContextMenuItems();
$(".table-context-menu").css({
left: getMenuPosition(event.clientX, 'width', 'scrollLeft'),
top: getMenuPosition(event.clientY, 'height', 'scrollTop')
@ -1265,6 +1272,7 @@ function showTableContextMenu(e) {
addToSelectedRows($(e).attr('data-rowId'));
$(e).addClass('table-active');
}
determineContextMenuItems();
}
function determineContextMenuItems() {
var tableRows = $('.table tbody tr:visible');
@ -1313,6 +1321,17 @@ function getMenuPosition(mouse, direction, scrollDir) {
position -= menu;
return position;
}
function getGarageMenuPosition(mouse, direction, scrollDir) {
var win = $(window)[direction](),
scroll = $(window)[scrollDir](),
menu = $(".garage-context-menu")[direction](),
position = mouse + scroll;
// opening menu would pass the side of the page
if (mouse + menu > win && menu < mouse)
position -= menu;
return position;
}
function handleTableRowClick(e, callBack, rowId) {
var selectMode = $("#chkSelectMode").length > 0 ? $("#chkSelectMode").is(":checked") : false;
if (!(event.ctrlKey || event.metaKey || selectMode)) {