using AutoMapper; using PowderCoating.Shared.Constants; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using PowderCoating.Application.DTOs.Common; using PowderCoating.Application.DTOs.Maintenance; using PowderCoating.Core.Entities; using PowderCoating.Core.Enums; using PowderCoating.Core.Interfaces; namespace PowderCoating.Web.Controllers; [Authorize(Policy = AppConstants.Policies.CanManageMaintenance)] public class MaintenanceController : Controller { private readonly IUnitOfWork _unitOfWork; private readonly IMapper _mapper; private readonly ITenantContext _tenantContext; private readonly ILogger _logger; public MaintenanceController( IUnitOfWork unitOfWork, IMapper mapper, ITenantContext tenantContext, ILogger logger) { _unitOfWork = unitOfWork; _mapper = mapper; _tenantContext = tenantContext; _logger = logger; } /// /// Displays the paginated maintenance record list with optional filters for equipment, /// keyword search, status, pending-only (Scheduled/InProgress/Overdue), and /// upcoming-only (due within 7 days or already overdue). The filter combinations are /// built via explicit lambda expressions rather than dynamic LINQ so the EF query /// can be translated to parameterized SQL without string concatenation risks. /// Equipment is eagerly loaded so the equipment name is available in the list without /// a secondary query per row. /// public async Task Index( int? equipmentId, string? searchTerm, MaintenanceStatus? statusFilter, string? sortColumn, string sortDirection = "asc", bool pendingOnly = false, bool upcomingOnly = false, int pageNumber = 1, int pageSize = 25) { try { // Create and validate grid request var gridRequest = new GridRequest { PageNumber = pageNumber, PageSize = pageSize, SortColumn = sortColumn ?? "ScheduledDate", SortDirection = sortColumn == null ? "asc" : sortDirection, SearchTerm = searchTerm }; gridRequest.Validate(); var today = DateTime.Today; var lookAhead = today.AddDays(7); // Build filter expression System.Linq.Expressions.Expression>? filter = null; if (upcomingOnly) { filter = m => (m.Status == MaintenanceStatus.Scheduled || m.Status == MaintenanceStatus.InProgress || m.Status == MaintenanceStatus.Overdue) && (m.Status == MaintenanceStatus.Overdue || m.ScheduledDate <= lookAhead); } else if (pendingOnly) { filter = m => m.Status == MaintenanceStatus.Scheduled || m.Status == MaintenanceStatus.InProgress || m.Status == MaintenanceStatus.Overdue; } else if (equipmentId.HasValue && !string.IsNullOrWhiteSpace(searchTerm) && statusFilter.HasValue) { var search = searchTerm.ToLower(); var status = statusFilter.Value; var eqId = equipmentId.Value; filter = m => m.EquipmentId == eqId && (m.MaintenanceType.ToLower().Contains(search) || (m.Description != null && m.Description.ToLower().Contains(search)) || (m.PerformedById != null && m.PerformedById.ToLower().Contains(search))) && m.Status == status; } else if (equipmentId.HasValue && !string.IsNullOrWhiteSpace(searchTerm)) { var search = searchTerm.ToLower(); var eqId = equipmentId.Value; filter = m => m.EquipmentId == eqId && (m.MaintenanceType.ToLower().Contains(search) || (m.Description != null && m.Description.ToLower().Contains(search)) || (m.PerformedById != null && m.PerformedById.ToLower().Contains(search))); } else if (equipmentId.HasValue && statusFilter.HasValue) { var eqId = equipmentId.Value; var status = statusFilter.Value; filter = m => m.EquipmentId == eqId && m.Status == status; } else if (!string.IsNullOrWhiteSpace(searchTerm) && statusFilter.HasValue) { var search = searchTerm.ToLower(); var status = statusFilter.Value; filter = m => (m.MaintenanceType.ToLower().Contains(search) || (m.Description != null && m.Description.ToLower().Contains(search)) || (m.PerformedById != null && m.PerformedById.ToLower().Contains(search))) && m.Status == status; } else if (equipmentId.HasValue) { var eqId = equipmentId.Value; filter = m => m.EquipmentId == eqId; } else if (!string.IsNullOrWhiteSpace(searchTerm)) { var search = searchTerm.ToLower(); filter = m => m.MaintenanceType.ToLower().Contains(search) || (m.Description != null && m.Description.ToLower().Contains(search)) || (m.PerformedById != null && m.PerformedById.ToLower().Contains(search)); } else if (statusFilter.HasValue) { var status = statusFilter.Value; filter = m => m.Status == status; } // Build orderBy function Func, IOrderedQueryable> orderBy = gridRequest.SortColumn switch { "MaintenanceType" => q => gridRequest.SortDirection == "asc" ? q.OrderBy(m => m.MaintenanceType) : q.OrderByDescending(m => m.MaintenanceType), "Status" => q => gridRequest.SortDirection == "asc" ? q.OrderBy(m => m.Status) : q.OrderByDescending(m => m.Status), "Priority" => q => gridRequest.SortDirection == "asc" ? q.OrderBy(m => m.Priority) : q.OrderByDescending(m => m.Priority), "ScheduledDate" => q => gridRequest.SortDirection == "asc" ? q.OrderBy(m => m.ScheduledDate) : q.OrderByDescending(m => m.ScheduledDate), "CompletedDate" => q => gridRequest.SortDirection == "asc" ? q.OrderBy(m => m.CompletedDate) : q.OrderByDescending(m => m.CompletedDate), "Cost" => q => gridRequest.SortDirection == "asc" ? q.OrderBy(m => m.TotalCost) : q.OrderByDescending(m => m.TotalCost), _ => q => q.OrderByDescending(m => m.ScheduledDate) }; // Get paged data with Equipment eager loading var (items, totalCount) = await _unitOfWork.MaintenanceRecords.GetPagedAsync( gridRequest.PageNumber, gridRequest.PageSize, filter, orderBy, m => m.Equipment); // Map to DTOs var maintenanceDtos = _mapper.Map>(items); var pagedResult = PagedResult.From(gridRequest, maintenanceDtos, totalCount); // Get equipment name if filtering by equipment if (equipmentId.HasValue) { var equipment = await _unitOfWork.Equipment.GetByIdAsync(equipmentId.Value); ViewBag.EquipmentName = equipment?.EquipmentName; } // Set ViewBag for sorting and filters ViewBag.EquipmentId = equipmentId; ViewBag.SearchTerm = searchTerm; ViewBag.StatusFilter = statusFilter; ViewBag.PendingOnly = pendingOnly; ViewBag.UpcomingOnly = upcomingOnly; ViewBag.SortColumn = gridRequest.SortColumn; ViewBag.SortDirection = gridRequest.SortDirection; return View(pagedResult); } catch (Exception ex) { _logger.LogError(ex, "Error retrieving maintenance records"); TempData["Error"] = "An error occurred while loading maintenance records."; return View(new PagedResult()); } } /// /// Renders the maintenance record detail page. If the record belongs to a recurrence /// series (identified by RecurrenceGroupId), the total series count is loaded and /// passed to the view so staff know how many scheduled occurrences exist in the group, /// helping them decide whether to edit or delete just this record or the whole series. /// public async Task Details(int? id) { if (id == null) { return NotFound(); } try { var maintenance = await _unitOfWork.MaintenanceRecords.GetByIdAsync(id.Value); if (maintenance == null) { return NotFound(); } var maintenanceDto = _mapper.Map(maintenance); // Count series siblings so Details view can display "X occurrences in this series" if (!string.IsNullOrEmpty(maintenance.RecurrenceGroupId)) { var groupId = maintenance.RecurrenceGroupId; var seriesRecords = await _unitOfWork.MaintenanceRecords.FindAsync( m => m.RecurrenceGroupId == groupId); ViewBag.SeriesCount = seriesRecords.Count(); } return View(maintenanceDto); } catch (Exception ex) { _logger.LogError(ex, "Error retrieving maintenance record {MaintenanceId}", id); TempData["Error"] = "An error occurred while loading the maintenance record."; return RedirectToAction(nameof(Index)); } } /// /// Renders the maintenance record creation form, defaulting status to Scheduled, /// priority to Normal, and the scheduled date to today. If an equipmentId is supplied /// (e.g. linked from the Equipment Details page), it pre-selects that equipment in the /// dropdown so staff do not need to choose it manually. /// public async Task Create(int? equipmentId) { try { var dto = new CreateMaintenanceDto { Status = MaintenanceStatus.Scheduled.ToString(), Priority = MaintenancePriority.Normal.ToString(), ScheduledDate = DateTime.Now.Date }; if (equipmentId.HasValue) { dto.EquipmentId = equipmentId.Value; } await PopulateViewBagAsync(equipmentId); return View(dto); } catch (Exception ex) { _logger.LogError(ex, "Error loading create maintenance form"); TempData["Error"] = "An error occurred while loading the form."; return RedirectToAction(nameof(Index)); } } /// /// Persists a new maintenance record. If the record is marked as recurring, the parent /// is saved first to obtain an Id, then a RecurrenceGroupId is assigned and /// creates all child occurrences in a /// second save. This two-phase save is necessary because the child records reference /// the parent's Id as RecurrenceParentId. After creation, redirects back to the /// Equipment Details page if the record was created from that context. /// [HttpPost] [ValidateAntiForgeryToken] public async Task Create(CreateMaintenanceDto dto) { if (!ModelState.IsValid) { await PopulateViewBagAsync(dto.EquipmentId); return View(dto); } try { var maintenance = _mapper.Map(dto); maintenance.CreatedAt = DateTime.UtcNow; await _unitOfWork.MaintenanceRecords.AddAsync(maintenance); await _unitOfWork.SaveChangesAsync(); // Generate recurring occurrences after parent is saved (so we have its Id) if (dto.IsRecurring && dto.RecurrenceFrequency.HasValue) { maintenance.RecurrenceGroupId = Guid.NewGuid().ToString(); await _unitOfWork.MaintenanceRecords.UpdateAsync(maintenance); await _unitOfWork.SaveChangesAsync(); await GenerateRecurringOccurrencesAsync(maintenance); TempData["Success"] = "Recurring maintenance series created successfully."; } else { TempData["Success"] = "Maintenance record created successfully."; } // Redirect back to equipment details if came from there if (dto.EquipmentId > 0) { return RedirectToAction("Details", "Equipment", new { id = dto.EquipmentId }); } return RedirectToAction(nameof(Index)); } catch (Exception ex) { _logger.LogError(ex, "Error creating maintenance record"); TempData["Error"] = "An error occurred while creating the maintenance record."; await PopulateViewBagAsync(dto.EquipmentId); return View(dto); } } /// /// Renders the maintenance record edit form. Sets ViewBag.IsRecurringSeries so the /// view can show a warning when editing a record that is part of a series, letting /// staff decide between editing just this occurrence or the whole series. /// public async Task Edit(int? id) { if (id == null) { return NotFound(); } try { var maintenance = await _unitOfWork.MaintenanceRecords.GetByIdAsync(id.Value); if (maintenance == null) { return NotFound(); } var dto = _mapper.Map(maintenance); ViewBag.IsRecurringSeries = !string.IsNullOrEmpty(maintenance.RecurrenceGroupId); await PopulateViewBagAsync(dto.EquipmentId); return View(dto); } catch (Exception ex) { _logger.LogError(ex, "Error retrieving maintenance record {MaintenanceId} for edit", id); TempData["Error"] = "An error occurred while loading the maintenance record."; return RedirectToAction(nameof(Index)); } } /// /// Persists edits to a maintenance record. Detects recurrence changes (frequency, /// end date, or the IsRecurring toggle) by comparing old values captured before /// AutoMapper overwrites them. When a change is detected, all future unfinished /// siblings in the old series are soft-deleted via /// and the current record becomes the new series parent, triggering a fresh call to /// . Completed and Cancelled records /// in the old series are preserved to maintain historical accuracy. /// [HttpPost] [ValidateAntiForgeryToken] public async Task Edit(int id, UpdateMaintenanceDto dto) { if (id != dto.Id) { return NotFound(); } if (!ModelState.IsValid) { await PopulateViewBagAsync(dto.EquipmentId); return View(dto); } try { var maintenance = await _unitOfWork.MaintenanceRecords.GetByIdAsync(id); if (maintenance == null) { return NotFound(); } // Capture old recurrence settings before overwriting var oldIsRecurring = maintenance.IsRecurring; var oldFrequency = maintenance.RecurrenceFrequency; var oldEndDate = maintenance.RecurrenceEndDate; var oldGroupId = maintenance.RecurrenceGroupId; _mapper.Map(dto, maintenance); maintenance.UpdatedAt = DateTime.UtcNow; // Detect recurrence changes bool recurrenceChanged = oldIsRecurring != dto.IsRecurring || oldFrequency != dto.RecurrenceFrequency || oldEndDate != dto.RecurrenceEndDate; if (recurrenceChanged) { // Delete all future unfinished siblings (not this record) if (!string.IsNullOrEmpty(oldGroupId)) { await DeleteFutureSeriesAsync(oldGroupId, excludeId: id); } if (dto.IsRecurring && dto.RecurrenceFrequency.HasValue) { // This record becomes the new series parent if (string.IsNullOrEmpty(maintenance.RecurrenceGroupId)) maintenance.RecurrenceGroupId = Guid.NewGuid().ToString(); maintenance.RecurrenceParentId = null; await _unitOfWork.MaintenanceRecords.UpdateAsync(maintenance); await _unitOfWork.SaveChangesAsync(); await GenerateRecurringOccurrencesAsync(maintenance); TempData["Success"] = "Maintenance record updated and recurrence series regenerated."; } else { // Toggled OFF — clear recurrence fields maintenance.RecurrenceGroupId = null; maintenance.RecurrenceParentId = null; await _unitOfWork.MaintenanceRecords.UpdateAsync(maintenance); await _unitOfWork.SaveChangesAsync(); TempData["Success"] = "Maintenance record updated. Recurrence has been removed."; } } else { await _unitOfWork.MaintenanceRecords.UpdateAsync(maintenance); await _unitOfWork.SaveChangesAsync(); TempData["Success"] = "Maintenance record updated successfully."; } return RedirectToAction(nameof(Details), new { id }); } catch (Exception ex) { _logger.LogError(ex, "Error updating maintenance record {MaintenanceId}", id); TempData["Error"] = "An error occurred while updating the maintenance record."; await PopulateViewBagAsync(dto.EquipmentId); return View(dto); } } /// /// Renders the delete confirmation page. For recurring series records, counts and /// passes to the view the number of future Scheduled/Overdue siblings so staff can /// make an informed choice between deleting just this occurrence or the entire series. /// public async Task Delete(int? id) { if (id == null) { return NotFound(); } try { var maintenance = await _unitOfWork.MaintenanceRecords.GetByIdAsync(id.Value); if (maintenance == null) { return NotFound(); } var maintenanceDto = _mapper.Map(maintenance); // Count deletable future series records so the view can show the user if (!string.IsNullOrEmpty(maintenance.RecurrenceGroupId)) { var groupId = maintenance.RecurrenceGroupId; var futureScheduled = await _unitOfWork.MaintenanceRecords.FindAsync( m => m.RecurrenceGroupId == groupId && m.Id != id.Value && (m.Status == MaintenanceStatus.Scheduled || m.Status == MaintenanceStatus.Overdue)); ViewBag.SeriesDeletableCount = futureScheduled.Count(); } return View(maintenanceDto); } catch (Exception ex) { _logger.LogError(ex, "Error retrieving maintenance record {MaintenanceId} for delete", id); TempData["Error"] = "An error occurred while loading the maintenance record."; return RedirectToAction(nameof(Index)); } } /// /// Soft-deletes a maintenance record or an entire recurring series depending on /// deleteMode ("single" or "series"). When "series" is requested, /// removes all records sharing the same /// RecurrenceGroupId, including already-completed ones. After deletion, redirects /// to the parent equipment's Details page so staff stay in context. /// [HttpPost, ActionName("Delete")] [ValidateAntiForgeryToken] public async Task DeleteConfirmed(int id, string deleteMode = "single") { try { var maintenance = await _unitOfWork.MaintenanceRecords.GetByIdAsync(id); if (maintenance == null) { return NotFound(); } var equipmentId = maintenance.EquipmentId; if (deleteMode == "series" && !string.IsNullOrEmpty(maintenance.RecurrenceGroupId)) { // Delete ALL records in the series (including this one) await DeleteEntireSeriesAsync(maintenance.RecurrenceGroupId); TempData["Success"] = "Entire maintenance series deleted successfully."; } else { // Single record delete await _unitOfWork.MaintenanceRecords.SoftDeleteAsync(maintenance); await _unitOfWork.SaveChangesAsync(); TempData["Success"] = "Maintenance record deleted successfully."; } return RedirectToAction("Details", "Equipment", new { id = equipmentId }); } catch (Exception ex) { _logger.LogError(ex, "Error deleting maintenance record {MaintenanceId}", id); TempData["Error"] = "An error occurred while deleting the maintenance record."; return RedirectToAction(nameof(Index)); } } /// /// Marks a maintenance record as Completed via an AJAX form submission and returns a /// JSON result so the Details page can update in-place without a full reload. Also /// updates the parent equipment's LastMaintenanceDate and calculates the next /// scheduled maintenance by adding RecommendedMaintenanceIntervalDays to the current /// timestamp — keeping the equipment status current without a separate scheduler job. /// Returns JSON { success, message } so the JS caller can show the appropriate toast. /// [HttpPost] [ValidateAntiForgeryToken] public async Task Complete(int id, string workPerformed, string partsReplaced, decimal laborCost, decimal partsCost, decimal downtimeHours) { try { var maintenance = await _unitOfWork.MaintenanceRecords.GetByIdAsync(id); if (maintenance == null) { return Json(new { success = false, message = "Maintenance record not found." }); } // Update maintenance record maintenance.Status = MaintenanceStatus.Completed; maintenance.CompletedDate = DateTime.UtcNow; maintenance.WorkPerformed = workPerformed; maintenance.PartsReplaced = partsReplaced; maintenance.LaborCost = laborCost; maintenance.PartsCost = partsCost; maintenance.TotalCost = laborCost + partsCost; maintenance.DowntimeHours = downtimeHours; maintenance.UpdatedAt = DateTime.UtcNow; await _unitOfWork.MaintenanceRecords.UpdateAsync(maintenance); // Update equipment maintenance dates var equipment = await _unitOfWork.Equipment.GetByIdAsync(maintenance.EquipmentId); if (equipment != null) { equipment.LastMaintenanceDate = DateTime.UtcNow; // Calculate next scheduled maintenance if (equipment.RecommendedMaintenanceIntervalDays > 0) { equipment.NextScheduledMaintenance = DateTime.UtcNow.AddDays(equipment.RecommendedMaintenanceIntervalDays); } equipment.UpdatedAt = DateTime.UtcNow; await _unitOfWork.Equipment.UpdateAsync(equipment); } await _unitOfWork.SaveChangesAsync(); TempData["Success"] = "Maintenance marked as completed successfully."; return Json(new { success = true, message = "Maintenance completed successfully." }); } catch (Exception ex) { _logger.LogError(ex, "Error completing maintenance record {MaintenanceId}", id); return Json(new { success = false, message = "An error occurred while completing the maintenance." }); } } // ──────────────────────────────────────────────── // Recurrence helpers // ──────────────────────────────────────────────── /// /// Generates child MaintenanceRecord occurrences for a recurring series, starting one /// interval after the parent's ScheduledDate. The parent must already be persisted so /// its Id can be stored as RecurrenceParentId on each child. If no explicit end date /// is set, a sensible default horizon is applied per frequency (e.g. 90 days for daily, /// 3 years for quarterly) to prevent accidentally creating thousands of records. A hard /// cap of 365 occurrences is enforced as an additional safeguard. /// private async Task GenerateRecurringOccurrencesAsync(MaintenanceRecord parent) { if (!parent.RecurrenceFrequency.HasValue) return; var frequency = parent.RecurrenceFrequency.Value; var startDate = parent.ScheduledDate; // Compute the end date: explicit setting or a sensible default horizon DateTime endDate = parent.RecurrenceEndDate ?? frequency switch { MaintenanceRecurrenceFrequency.Daily => startDate.AddDays(90), MaintenanceRecurrenceFrequency.Weekly => startDate.AddDays(364), MaintenanceRecurrenceFrequency.BiWeekly => startDate.AddDays(364), MaintenanceRecurrenceFrequency.Monthly => startDate.AddMonths(24), MaintenanceRecurrenceFrequency.Quarterly => startDate.AddYears(3), MaintenanceRecurrenceFrequency.Annually => startDate.AddYears(3), MaintenanceRecurrenceFrequency.BiAnnually => startDate.AddMonths(18), _ => startDate.AddYears(1) }; var occurrences = new List(); var nextDate = AdvanceDate(startDate, frequency); int maxOccurrences = 365; int count = 0; while (nextDate <= endDate && count < maxOccurrences) { occurrences.Add(nextDate); nextDate = AdvanceDate(nextDate, frequency); count++; } foreach (var date in occurrences) { var child = new MaintenanceRecord { EquipmentId = parent.EquipmentId, MaintenanceType = parent.MaintenanceType, Priority = parent.Priority, Description = parent.Description, AssignedUserId = parent.AssignedUserId, CompanyId = parent.CompanyId, Notes = parent.Notes, LaborCost = parent.LaborCost, PartsCost = parent.PartsCost, TotalCost = parent.TotalCost, Status = MaintenanceStatus.Scheduled, ScheduledDate = date, // Recurrence linkage IsRecurring = true, RecurrenceFrequency = parent.RecurrenceFrequency, RecurrenceEndDate = parent.RecurrenceEndDate, RecurrenceGroupId = parent.RecurrenceGroupId, RecurrenceParentId = parent.Id, CreatedAt = DateTime.UtcNow }; await _unitOfWork.MaintenanceRecords.AddAsync(child); } await _unitOfWork.SaveChangesAsync(); } /// /// Soft-deletes all future unfinished (Scheduled or Overdue) records that share the /// given RecurrenceGroupId, optionally excluding one record (typically the one being /// edited). Completed, InProgress, and Cancelled records are intentionally preserved /// so that historical maintenance data and cost totals remain intact. /// private async Task DeleteFutureSeriesAsync(string recurrenceGroupId, int? excludeId = null) { var siblings = await _unitOfWork.MaintenanceRecords.FindAsync( m => m.RecurrenceGroupId == recurrenceGroupId && (m.Status == MaintenanceStatus.Scheduled || m.Status == MaintenanceStatus.Overdue) && (excludeId == null || m.Id != excludeId.Value)); foreach (var record in siblings) { await _unitOfWork.MaintenanceRecords.SoftDeleteAsync(record); } await _unitOfWork.SaveChangesAsync(); } /// /// Soft-deletes every record sharing the given RecurrenceGroupId, including completed /// and in-progress ones. Used exclusively for the "delete entire series" confirmation /// path. Unlike , historical records are not /// spared because the operator has explicitly chosen to erase the whole series. /// private async Task DeleteEntireSeriesAsync(string recurrenceGroupId) { var all = await _unitOfWork.MaintenanceRecords.FindAsync( m => m.RecurrenceGroupId == recurrenceGroupId); foreach (var record in all) { await _unitOfWork.MaintenanceRecords.SoftDeleteAsync(record); } await _unitOfWork.SaveChangesAsync(); } /// /// Returns the next occurrence date by advancing by one /// interval for the given frequency. Used iteratively by /// to build the full occurrence list. /// BiAnnually means every 6 months (twice a year), not every 2 years. /// private static DateTime AdvanceDate(DateTime from, MaintenanceRecurrenceFrequency frequency) => frequency switch { MaintenanceRecurrenceFrequency.Daily => from.AddDays(1), MaintenanceRecurrenceFrequency.Weekly => from.AddDays(7), MaintenanceRecurrenceFrequency.BiWeekly => from.AddDays(14), MaintenanceRecurrenceFrequency.Monthly => from.AddMonths(1), MaintenanceRecurrenceFrequency.Quarterly => from.AddMonths(3), MaintenanceRecurrenceFrequency.Annually => from.AddYears(1), MaintenanceRecurrenceFrequency.BiAnnually => from.AddMonths(6), _ => from.AddDays(7) }; /// /// Loads the equipment dropdown and the status/priority enum arrays into ViewBag for /// use by the Create and Edit forms. Only active equipment is shown in the dropdown /// because scheduling maintenance for retired or out-of-service equipment is not /// meaningful and would clutter the list. /// private async Task PopulateViewBagAsync(int? selectedEquipmentId = null) { var companyId = _tenantContext.GetCurrentCompanyId() ?? 0; var equipment = await _unitOfWork.Equipment.FindAsync(e => e.CompanyId == companyId); ViewBag.EquipmentList = new SelectList( equipment.Where(e => e.IsActive).OrderBy(e => e.EquipmentName), "Id", "EquipmentName", selectedEquipmentId); ViewBag.StatusList = Enum.GetValues(); ViewBag.PriorityList = Enum.GetValues(); } }