diff --git a/Web/Attributes/OptionLabelAttribute.cs b/Web/Attributes/OptionLabelAttribute.cs new file mode 100644 index 0000000..4b1727c --- /dev/null +++ b/Web/Attributes/OptionLabelAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace MileageTraker.Web.Attributes +{ + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] + public class OptionLabelAttribute : Attribute + { + public string Text { get; private set; } + + public OptionLabelAttribute(string text) + { + Text = text; + } + } +} \ No newline at end of file diff --git a/Web/Context/MileageTrakerContext.cs b/Web/Context/MileageTrakerContext.cs index 70ac7b9..e7983c1 100644 --- a/Web/Context/MileageTrakerContext.cs +++ b/Web/Context/MileageTrakerContext.cs @@ -13,6 +13,7 @@ namespace MileageTraker.Web.Context public DbSet Roles { get; set; } public DbSet PurposeTypes { get; set; } public DbSet FuelLogs { get; set; } + public DbSet VehicleRecalls { get; set; } public DbSet VehicleServices { get; set; } public DbSet ServiceReminders { get; set; } diff --git a/Web/Controllers/VehicleController.cs b/Web/Controllers/VehicleController.cs index ae400dd..0418014 100644 --- a/Web/Controllers/VehicleController.cs +++ b/Web/Controllers/VehicleController.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Web; using System.Web.Mvc; using MileageTraker.Web.Attributes; using MileageTraker.Web.DAL; @@ -32,7 +33,16 @@ namespace MileageTraker.Web.Controllers public PartialViewResult DetailsPartial(string id) { var vehicle = DataService.GetVehicle(id); - return PartialView(new VehiclePartialDetails(vehicle)); + if (vehicle == null) + Response.StatusCode = 404; + return PartialView(new VehiclePartialDetails(vehicle)); + } + public PartialViewResult DetailsQuickPartial(string id) + { + var vehicle = DataService.GetVehicle(id); + if (vehicle == null) + Response.StatusCode = 404; + return PartialView(new VehiclePartialDetailsQuick(vehicle)); } public ActionResult Create() diff --git a/Web/Controllers/VehicleRecallController.cs b/Web/Controllers/VehicleRecallController.cs new file mode 100644 index 0000000..fe1cd54 --- /dev/null +++ b/Web/Controllers/VehicleRecallController.cs @@ -0,0 +1,104 @@ +using System.Linq; +using System.Web.Mvc; +using MileageTraker.Web.Attributes; +using MileageTraker.Web.DAL; +using MileageTraker.Web.Utility; +using MileageTraker.Web.ViewModels.ServiceReminder; +using MileageTraker.Web.ViewModels.VehicleRecall; + +namespace MileageTraker.Web.Controllers +{ + [Authorize(Roles = "Administrator, Developer")] + public class VehicleRecallController : ControllerBase + { + public ActionResult Index() + { + var vehicleRecalls = DataService.GetOpenVehicleRecalls().ToList(); + + if (vehicleRecalls.Count == 0) + return View("Empty"); + + var viewModel = vehicleRecalls.Select(vr => new VehicleRecallViewModel(vr)); + + return View(viewModel); + } + + public ViewResult Details(int id) + { + var vehicleRecall = DataService.GetVehicleRecall(id); + var viewModel = new VehicleRecallViewModel(vehicleRecall); + return View(viewModel); + } + + public ActionResult Create() + { + return View(); + } + + [HttpGet] + [RequireRequestValue("vehicleId")] + public ActionResult Create(string vehicleId) + { + return View(new VehicleRecallViewModel{VehicleId = vehicleId}); + } + + [HttpPost] + [ActionLog] + public ActionResult Create(VehicleRecallViewModel viewModel) + { + if (ModelState.IsValid) + { + var vehicleRecall = viewModel.GetVehicleRecall(); + + vehicleRecall.Vehicle = DataService.GetVehicle(viewModel.VehicleId); + DataService.AddVehicleRecall(vehicleRecall); + + SetStatusMessage( + string.Format("Vehicle Recall for vehicle {0} created", viewModel.VehicleId), StatusType.Success); + return RedirectToAction("Index"); + } + + return View(viewModel); + } + + public ActionResult Edit(int id) + { + var vehicleRecall = DataService.GetVehicleRecall(id); + var viewModel = new VehicleRecallViewModel(vehicleRecall); + return View(viewModel); + } + + public ActionResult Delete(int id) + { + var vehicleRecall = DataService.GetVehicleRecall(id); + var viewModel = new VehicleRecallViewModel(vehicleRecall); + return View(viewModel); + } + + [HttpPost, ActionName("Delete")] + [ActionLog] + public ActionResult DeleteConfirmed(int id) + { + DataService.DeleteVehicleRecall(id); + + SetStatusMessage("Vehicle Recall deleted"); + return RedirectToAction("Index"); + } + + [HttpPost] + [ActionLog] + public ActionResult Edit(VehicleRecallViewModel viewModel) + { + if (ModelState.IsValid) + { + var vehicleRecall = viewModel.GetVehicleRecall(); + vehicleRecall.Vehicle = DataService.GetVehicle(viewModel.VehicleId); + + DataService.UpdateVehicleRecall(vehicleRecall); + SetStatusMessage("Changes saved for vehicle recall " + vehicleRecall.Vehicle.VehicleId); + return RedirectToAction("Details", new { id = vehicleRecall.VehicleRecallId }); + } + return View(viewModel); + } + } +} \ No newline at end of file diff --git a/Web/Controllers/VehicleServiceController.cs b/Web/Controllers/VehicleServiceController.cs index a5f0731..f3b1191 100644 --- a/Web/Controllers/VehicleServiceController.cs +++ b/Web/Controllers/VehicleServiceController.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Web.Mvc; using MileageTraker.Web.Attributes; @@ -68,55 +69,97 @@ namespace MileageTraker.Web.Controllers { var vehicleService = DataService.GetVehicleService(id); var viewModel = new VehicleServiceViewModel(vehicleService); - return View(viewModel); + return View(HydrateViewModel(viewModel, vehicleServiceId : vehicleService.VehicleServiceId)); } [Authorize(Roles = "Administrator, Developer")] public ActionResult Create() { - return View(); + return View(HydrateViewModel(new VehicleServiceViewModel())); } [Authorize(Roles = "Administrator, Developer")] [HttpGet] [RequireRequestValue("vehicleId")] public ActionResult Create(string vehicleId) - { - return View(new VehicleServiceViewModel{VehicleId = vehicleId}); + { + return View(HydrateViewModel(new VehicleServiceViewModel(), vehicleId: vehicleId)); } [HttpPost] [RequireRequestValue("serviceVehicleId")] public ActionResult CreateDriver(string serviceVehicleId) { - return View ("Create", new VehicleServiceViewModel{VehicleId = serviceVehicleId }); + return View ("Create", HydrateViewModel(new VehicleServiceViewModel(), vehicleId: serviceVehicleId)); } - [HttpPost] + private VehicleServiceViewModel HydrateViewModel(VehicleServiceViewModel viewModel, + int vehicleServiceId = Int32.MinValue, string vehicleId = null) + { + if (vehicleId != null) + { + viewModel.VehicleId = vehicleId; + } + viewModel.VehicleRecall + = new SelectListViewModel + { + Available = GetVehicleRecallSelectList( + viewModel.VehicleId, + vehicleServiceId) + }; + viewModel.VehicleRecall.Selected = + viewModel.VehicleRecall.Available.SelectedValue != null + ? (int?) int.Parse((string)viewModel.VehicleRecall.Available.SelectedValue) + : null; + + return viewModel; + } + + private SelectList GetVehicleRecallSelectList(string vehicleId = null, int vehicleServiceId = Int32.MinValue) + { + var availableRecalls = + DataService.GetOpenVehicleRecalls(vehicleId).Union(DataService.GetVehicleRecallByServiceId(vehicleServiceId)) + .Select(vr => + new SelectListItem + { + Text = vr.Identifier, + Value = vr.VehicleRecallId.ToString(), + Selected = vr.CompletedService.VehicleServiceId == vehicleServiceId + }).ToList(); + + var selectedValue = availableRecalls.FirstOrDefault(vr => vr.Selected)?.Value ?? "0"; + + var selectList = new SelectList(availableRecalls, "Value", "Text", selectedValue); + return selectList; + } + + [HttpPost] [ActionLog] public ActionResult Create(VehicleServiceViewModel viewModel) { - if (ModelState.IsValid) - { + if (ModelState.IsValid) + { var vehicleService = viewModel.GetVehicleService(); vehicleService.Vehicle = DataService.GetVehicle(viewModel.VehicleId); DataService.AddVehicleService(vehicleService); - SetStatusMessage( - string.Format("Vehicle Service for vehicle {0} created", viewModel.VehicleId), StatusType.Success); + // update any recalls + DataService.UpdateVehicleRecallService(viewModel.VehicleRecall?.Selected ?? int.MinValue, vehicleService); + + SetStatusMessage($"Vehicle Service for vehicle {viewModel.VehicleId} created", StatusType.Success); return RedirectToAction("UpdateServiceReminders", new {vehicleId = viewModel.VehicleId}); } - - return View(viewModel); + + return View(HydrateViewModel(viewModel)); } - [HttpGet] + [HttpGet] public ActionResult UpdateServiceReminders(string vehicleId) { var vehicle = DataService.GetVehicle(vehicleId); - var viewModel = new UpdateServiceRemindersViewModel { VehicleId = vehicleId }; + var viewModel = new UpdateServiceRemindersViewModel { VehicleId = vehicleId}; RefreshServiceReminderViewModel(viewModel, vehicle, true); @@ -208,9 +251,9 @@ namespace MileageTraker.Web.Controllers [Authorize(Roles = "Administrator, Developer")] public ActionResult Edit(int id) { - var vehicleService = DataService.GetVehicleService(id); + var vehicleService = DataService.GetVehicleService(id); var viewModel = new VehicleServiceViewModel(vehicleService); - return View(viewModel); + return View(HydrateViewModel(viewModel, vehicleServiceId: vehicleService.VehicleServiceId)); } [Authorize(Roles = "Administrator, Developer")] @@ -218,7 +261,7 @@ namespace MileageTraker.Web.Controllers { var vehicleService = DataService.GetVehicleService(id); var viewModel = new VehicleServiceViewModel(vehicleService); - return View(viewModel); + return View(HydrateViewModel(viewModel, vehicleServiceId: vehicleService.VehicleServiceId)); } [Authorize(Roles = "Administrator, Developer")] @@ -237,16 +280,19 @@ namespace MileageTraker.Web.Controllers [ActionLog] public ActionResult Edit(VehicleServiceViewModel viewModel) { + var vehicleService = viewModel.GetVehicleService(); if (ModelState.IsValid) { - var vehicleService = viewModel.GetVehicleService(); vehicleService.Vehicle = DataService.GetVehicle(viewModel.VehicleId); DataService.UpdateVehicleService(vehicleService); - SetStatusMessage("Changes saved for vehicle service " + vehicleService.Vehicle.VehicleId); + + DataService.UpdateVehicleRecallService(viewModel.VehicleRecall?.Selected ?? int.MinValue, vehicleService); + + SetStatusMessage("Changes saved for vehicle service " + vehicleService.Vehicle.VehicleId); return RedirectToAction("Details", new { id = vehicleService.VehicleServiceId }); } - return View(viewModel); + return View(HydrateViewModel(viewModel, vehicleService.VehicleServiceId)); } public JsonResult ServiceCenterNameAutocomplete(string term) diff --git a/Web/DAL/DataService.cs b/Web/DAL/DataService.cs index d28c8e3..cc8b7ab 100644 --- a/Web/DAL/DataService.cs +++ b/Web/DAL/DataService.cs @@ -820,6 +820,11 @@ namespace MileageTraker.Web.DAL var vehicleService = _db.VehicleServices.Find(id); _db.VehicleServices.Remove(vehicleService); + foreach (var recall in _db.VehicleRecalls.Where(vr => vr.CompletedService.VehicleServiceId == id)) + { + recall.CompletedService = null; + _db.Entry(recall).State = EntityState.Modified; + } _db.SaveChanges(); } @@ -990,7 +995,79 @@ namespace MileageTraker.Web.DAL public const int ServiceReminderDefaultMileageDelta = 5000; public const int ServiceReminderUpcomingMileageThreshold = 300; - #endregion + #endregion - } + #region Vehicle Recall + + public void AddVehicleRecall(VehicleRecall vehicleRecall) + { + _db.VehicleRecalls.Add(vehicleRecall); + _db.SaveChanges(); + } + + public void UpdateVehicleRecall(VehicleRecall vehicleRecall) + { + _db.Entry(vehicleRecall).State = EntityState.Modified; + _db.SaveChanges(); + } + + public void UpdateVehicleRecallService(int vehicleRecallId, VehicleService vehicleService) + { + if (vehicleRecallId > 0) + { + var recall = GetVehicleRecall(vehicleRecallId); + recall.CompletedService = vehicleService; + UpdateVehicleRecall(recall); + } + else + { + var recall = GetVehicleRecalls().FirstOrDefault(vr => vr.CompletedService.VehicleServiceId == vehicleService.VehicleServiceId); + if (recall != null) + { + recall.CompletedService = null; + UpdateVehicleRecall(recall); + } + } + } + + public IQueryable GetVehicleRecalls(string vehicleId) + { + return GetVehicleRecalls().Where(vr => vr.Vehicle.VehicleId == vehicleId); + } + + public IQueryable GetOpenVehicleRecalls() + { + return GetVehicleRecalls().Where(vr => vr.CompletedService == null); + } + + public IQueryable GetOpenVehicleRecalls(string vehicleId) + { + return GetOpenVehicleRecalls().Where(vr => vr.Vehicle.VehicleId == vehicleId); + } + + public IQueryable GetVehicleRecallByServiceId(int vehicleServiceId) + { + return GetVehicleRecalls().Where(vr => vr.CompletedService.VehicleServiceId == vehicleServiceId); + } + + public IQueryable GetVehicleRecalls() + { + return _db.VehicleRecalls; + } + + public VehicleRecall GetVehicleRecall(int id) + { + return _db.VehicleRecalls.Find(id); + } + + public void DeleteVehicleRecall(int id) + { + var vehicleRecall = _db.VehicleRecalls.Find(id); + + _db.VehicleRecalls.Remove(vehicleRecall); + _db.SaveChanges(); + } + + #endregion + } } \ No newline at end of file diff --git a/Web/Global.asax.cs b/Web/Global.asax.cs index 7ca54a9..766ed36 100644 --- a/Web/Global.asax.cs +++ b/Web/Global.asax.cs @@ -68,19 +68,22 @@ namespace MileageTraker.Web metadata.DisplayName = propertyName.Wordify(); var formatHint = attributes.OfType().FirstOrDefault(); - metadata.AdditionalValues.Add("FormatHint", formatHint != null ? formatHint.Text : null); + metadata.AdditionalValues.Add("FormatHint", formatHint?.Text); var unitsAttribute = attributes.OfType().FirstOrDefault(); - metadata.AdditionalValues.Add("Units", unitsAttribute != null ? unitsAttribute.Text : null); + metadata.AdditionalValues.Add("Units", unitsAttribute?.Text); var currencyAttribute = attributes.OfType().FirstOrDefault(); - metadata.AdditionalValues.Add("Currency", currencyAttribute != null ? currencyAttribute.Symbol : null); + metadata.AdditionalValues.Add("Currency", currencyAttribute?.Symbol); var inputSizeAttribute = attributes.OfType().FirstOrDefault(); - metadata.AdditionalValues.Add("InputSize", inputSizeAttribute != null ? inputSizeAttribute.ClassName : null); + metadata.AdditionalValues.Add("InputSize", inputSizeAttribute?.ClassName); var editLabelAttribute = attributes.OfType().FirstOrDefault(); metadata.AdditionalValues.Add("EditLabel", editLabelAttribute == null); + + var optionLabelAttribute = attributes.OfType().FirstOrDefault(); + metadata.AdditionalValues.Add("OptionLabel", optionLabelAttribute?.Text); return metadata; } diff --git a/Web/Migrations/202008300135173_VehicleRecall.Designer.cs b/Web/Migrations/202008300135173_VehicleRecall.Designer.cs new file mode 100644 index 0000000..9b5e7eb --- /dev/null +++ b/Web/Migrations/202008300135173_VehicleRecall.Designer.cs @@ -0,0 +1,29 @@ +// +namespace MileageTraker.Web.Migrations +{ + using System.CodeDom.Compiler; + using System.Data.Entity.Migrations; + using System.Data.Entity.Migrations.Infrastructure; + using System.Resources; + + [GeneratedCode("EntityFramework.Migrations", "6.2.0-61023")] + public sealed partial class VehicleRecall : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(VehicleRecall)); + + string IMigrationMetadata.Id + { + get { return "202008300135173_VehicleRecall"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/Web/Migrations/202008300135173_VehicleRecall.cs b/Web/Migrations/202008300135173_VehicleRecall.cs new file mode 100644 index 0000000..f265721 --- /dev/null +++ b/Web/Migrations/202008300135173_VehicleRecall.cs @@ -0,0 +1,37 @@ +namespace MileageTraker.Web.Migrations +{ + using System; + using System.Data.Entity.Migrations; + + public partial class VehicleRecall : DbMigration + { + public override void Up() + { + CreateTable( + "dbo.VehicleRecall", + c => new + { + VehicleRecallId = c.Int(nullable: false, identity: true), + Identifier = c.String(nullable: false, maxLength: 128), + Description = c.String(nullable: false, maxLength: 128), + CompletedService_VehicleServiceId = c.Int(), + Vehicle_VehicleId = c.String(nullable: false, maxLength: 6), + }) + .PrimaryKey(t => t.VehicleRecallId) + .ForeignKey("dbo.VehicleService", t => t.CompletedService_VehicleServiceId) + .ForeignKey("dbo.Vehicle", t => t.Vehicle_VehicleId, cascadeDelete: true) + .Index(t => t.CompletedService_VehicleServiceId) + .Index(t => t.Vehicle_VehicleId); + + } + + public override void Down() + { + DropForeignKey("dbo.VehicleRecall", "Vehicle_VehicleId", "dbo.Vehicle"); + DropForeignKey("dbo.VehicleRecall", "CompletedService_VehicleServiceId", "dbo.VehicleService"); + DropIndex("dbo.VehicleRecall", new[] { "Vehicle_VehicleId" }); + DropIndex("dbo.VehicleRecall", new[] { "CompletedService_VehicleServiceId" }); + DropTable("dbo.VehicleRecall"); + } + } +} diff --git a/Web/Migrations/202008300135173_VehicleRecall.resx b/Web/Migrations/202008300135173_VehicleRecall.resx new file mode 100644 index 0000000..7c60647 --- /dev/null +++ b/Web/Migrations/202008300135173_VehicleRecall.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 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 + + + dbo + + \ No newline at end of file diff --git a/Web/Models/VehicleRecall.cs b/Web/Models/VehicleRecall.cs new file mode 100644 index 0000000..73d4e58 --- /dev/null +++ b/Web/Models/VehicleRecall.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; + +namespace MileageTraker.Web.Models +{ + public class VehicleRecall + { + [Key] + public int VehicleRecallId { get; set; } + + [Required] + public virtual Vehicle Vehicle { get; set; } + + [Required] + [StringLength(128, MinimumLength = 3)] + public string Identifier { get; set; } + + [Required] + [StringLength(128, MinimumLength = 3, ErrorMessage = "Minimum 3 characters")] + public string Description { get; set; } + public virtual VehicleService CompletedService { get; set; } + } +} \ No newline at end of file diff --git a/Web/Models/VehicleService.cs b/Web/Models/VehicleService.cs index f185737..1ca2460 100644 --- a/Web/Models/VehicleService.cs +++ b/Web/Models/VehicleService.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; namespace MileageTraker.Web.Models { @@ -8,7 +9,7 @@ namespace MileageTraker.Web.Models [Key] public int VehicleServiceId { get; set; } - [Required] + [Required] public virtual Vehicle Vehicle { get; set; } [Required] @@ -24,10 +25,11 @@ namespace MileageTraker.Web.Models [StringLength(32)] public string InvoiceNumber { get; set; } + [Required] public decimal Price { get; set; } [StringLength(64, MinimumLength = 3, ErrorMessage = "Minimum 3 characters")] public string Description { get; set; } - } + } } \ No newline at end of file diff --git a/Web/Scripts/Shared/Site.js b/Web/Scripts/Shared/Site.js index c162609..a703c16 100644 --- a/Web/Scripts/Shared/Site.js +++ b/Web/Scripts/Shared/Site.js @@ -220,6 +220,7 @@ function addButtonIcons () { 'Cities': 'map', 'Purposes': 'arrow-right', 'Vehicle Service': 'wrench', + 'Vehicle Recall': 'refresh', 'Vehicle': 'car', 'Cost': 'money', 'Reminder': 'clock-o', @@ -385,5 +386,27 @@ $(function () { }); }); +function VehicleInput_ShowDetails() { + $(".control-group.vehicleId").append('
'); + + var getDetails = function () { + var vehicleId = $("#VehicleId"); + var vehicleDetails = $("#VehicleDetails"); + if (vehicleId.val().length >= 4 && vehicleId.valid() === true) { + $.ajax({ + type: "GET", + url: "/Vehicle/DetailsQuickPartial", + data: { id: vehicleId.val() }, + success: function (html) { vehicleDetails.html(html); }, + error: function () { vehicleDetails.empty(); } + }); + } else { + vehicleDetails.empty(); + } + }; + + $("#VehicleId").keyup(getDetails); +} + /* End Form Validation */ diff --git a/Web/ViewModels/SelectListViewModel.cs b/Web/ViewModels/SelectListViewModel.cs index a6d8ca7..e6db490 100644 --- a/Web/ViewModels/SelectListViewModel.cs +++ b/Web/ViewModels/SelectListViewModel.cs @@ -5,7 +5,7 @@ namespace MileageTraker.Web.ViewModels public class SelectListViewModel { public SelectList Available { get; set; } - public int Selected { get; set; } + public int? Selected { get; set; } public override string ToString() { diff --git a/Web/ViewModels/Vehicle/VehiclePartialDetailsQuick.cs b/Web/ViewModels/Vehicle/VehiclePartialDetailsQuick.cs new file mode 100644 index 0000000..2586000 --- /dev/null +++ b/Web/ViewModels/Vehicle/VehiclePartialDetailsQuick.cs @@ -0,0 +1,42 @@ +using System.ComponentModel.DataAnnotations; +using AutoMapper; + +namespace MileageTraker.Web.ViewModels.Vehicle +{ + public class VehiclePartialDetailsQuick + { + [Display(Name = "Year")] + public string ModelYear { get; set; } + + public string Make { get; set; } + + [Display(Name = "Model")] + public string CarModel { get; set; } + + public string Color { get; set; } + + public string Type { get; set; } + + [Display(Name = "VIN")] + public string Vin { get; set; } + + [Display(Name = "Tag#")] + public string TagNumber { get; set; } + + [DisplayFormat(NullDisplayText = "Unassigned")] + public string Assigned { get; set; } + + static VehiclePartialDetailsQuick() + { + Mapper.CreateMap(); + } + + public VehiclePartialDetailsQuick() + {} + + public VehiclePartialDetailsQuick(Models.Vehicle vehicle) + { + Mapper.Map(vehicle, this); + } + } +} \ No newline at end of file diff --git a/Web/ViewModels/VehicleRecall/VehicleRecallViewModel.cs b/Web/ViewModels/VehicleRecall/VehicleRecallViewModel.cs new file mode 100644 index 0000000..2e8f217 --- /dev/null +++ b/Web/ViewModels/VehicleRecall/VehicleRecallViewModel.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Web.Mvc; +using AutoMapper; +using MileageTraker.Web.Attributes; + +namespace MileageTraker.Web.ViewModels.VehicleRecall +{ + public class VehicleRecallViewModel + { + [HiddenInput(DisplayValue = false)] + public int? VehicleRecallId { get; set; } + + [Required] + [Remote("Exists", "Vehicle", ErrorMessage = "ID not found")] + [StringLength(6, MinimumLength = 4, ErrorMessage = "Must be at least a 4 digit number")] + [Display(Name = "Vehicle ID")] + [RegularExpression(@"\d+", ErrorMessage = "Vehicle ID must be all numbers")] + [InputSize("mini")] + public string VehicleId { get; set; } + + [Required] + [Display(Name = "Recall Identifier")] + [StringLength(128, MinimumLength = 3)] + [InputSize("small")] + public string Identifier { get; set; } + + [Required] + [StringLength(128, MinimumLength = 3, ErrorMessage = "Minimum 3 characters")] + public string Description { get; set; } + + [HiddenInput(DisplayValue = false)] + [Display(Name = "Vehicle Service")] + [UIHint("VehicleServiceLink")] + public int CompletedService_VehicleServiceId { get; set; } + + static VehicleRecallViewModel() + { + Mapper.CreateMap(); + Mapper.CreateMap() + .ForMember(dest => dest.VehicleId, opt => opt.MapFrom(src => src.Vehicle.VehicleId)); + } + + public VehicleRecallViewModel(Models.VehicleRecall vehicleRecall) + { + Mapper.Map(vehicleRecall, this); + } + + public VehicleRecallViewModel() + { + } + + public Models.VehicleRecall GetVehicleRecall() + { + var vehicleRecall = new Models.VehicleRecall(); + Mapper.Map(this, vehicleRecall); + return vehicleRecall; + } + } +} \ No newline at end of file diff --git a/Web/ViewModels/VehicleService/UpdateServiceRemindersViewModel.cs b/Web/ViewModels/VehicleService/UpdateServiceRemindersViewModel.cs index 001bbb6..ab567f7 100644 --- a/Web/ViewModels/VehicleService/UpdateServiceRemindersViewModel.cs +++ b/Web/ViewModels/VehicleService/UpdateServiceRemindersViewModel.cs @@ -4,7 +4,6 @@ using System.Web.Mvc; using AutoMapper; using MileageTraker.Web.Attributes; using MileageTraker.Web.DAL; -using MileageTraker.Web.ViewModels.ServiceReminder; namespace MileageTraker.Web.ViewModels.VehicleService { diff --git a/Web/ViewModels/VehicleService/VehicleServiceViewModel.cs b/Web/ViewModels/VehicleService/VehicleServiceViewModel.cs index 6db35e0..64337b0 100644 --- a/Web/ViewModels/VehicleService/VehicleServiceViewModel.cs +++ b/Web/ViewModels/VehicleService/VehicleServiceViewModel.cs @@ -1,13 +1,15 @@ using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Web.Mvc; using AutoMapper; using MileageTraker.Web.Attributes; +using MileageTraker.Web.DAL; namespace MileageTraker.Web.ViewModels.VehicleService { - public class VehicleServiceViewModel - { + public class VehicleServiceViewModel : IValidatableObject + { [HiddenInput(DisplayValue = false)] public int? VehicleServiceId { get; set; } @@ -41,6 +43,11 @@ namespace MileageTraker.Web.ViewModels.VehicleService [Currency] public decimal Price { get; set; } + [Display(Name = "Vehicle Recall")] + [OptionLabel("Not a recall")] + [UIHint("VehicleRecallSelectListViewModel")] + public SelectListViewModel VehicleRecall { get; set; } + [StringLength(64, MinimumLength = 3, ErrorMessage = "Minimum 3 characters")] public string Description { get; set; } @@ -51,14 +58,16 @@ namespace MileageTraker.Web.ViewModels.VehicleService .ForMember(dest => dest.VehicleId, opt => opt.MapFrom(src => src.Vehicle.VehicleId)); } - public VehicleServiceViewModel(Models.VehicleService vehicleService) + public VehicleServiceViewModel(Models.VehicleService vehicleService) : this() { Mapper.Map(vehicleService, this); } public VehicleServiceViewModel() - { - } + { + //var enumerable = new string[]{}; + //VehicleRecallId = new SelectListViewModel { Available = new SelectList(enumerable, "VehicleRecallId", "RecallId") }; + } public Models.VehicleService GetVehicleService() { @@ -66,5 +75,19 @@ namespace MileageTraker.Web.ViewModels.VehicleService Mapper.Map(this, vehicleService); return vehicleService; } - } + + public IEnumerable Validate(ValidationContext validationContext) + { + using (var dataService = new DataService()) + { + var recallId = VehicleRecall?.Selected ?? int.MinValue; + // if there's a recall, verify it's for this vehicle + if (recallId >= 0 && dataService.GetVehicleRecall(recallId).Vehicle.VehicleId != VehicleId) + { + yield return new ValidationResult("This recall is not for the given vehicle", + new[] {"VehicleRecall"}); + } + } + } + } } \ No newline at end of file diff --git a/Web/Views/ServiceReminder/Index.cshtml b/Web/Views/ServiceReminder/Index.cshtml index 9a34a9a..4597d30 100644 --- a/Web/Views/ServiceReminder/Index.cshtml +++ b/Web/Views/ServiceReminder/Index.cshtml @@ -17,7 +17,10 @@
- @Html.ActionLink("Add Reminder", "Create", new { vehicleId = Model.VehicleId }, new{@class="btn"}) - @Html.ActionLink("Add Service", "Create", "VehicleService", new { vehicleId = Model.VehicleId }, new { @class = "btn" }) - @Html.ActionLink("Vehicle Details", "Details", "Vehicle", new { id = Model.VehicleId }, new{@class="btn"}) +
+ @Html.ActionLink("Add Reminder", "Create", new { vehicleId = Model.VehicleId }, new { @class = "btn" }) + @Html.ActionLink("Add Service", "Create", "VehicleService", new { vehicleId = Model.VehicleId }, new { @class = "btn" }) + @Html.ActionLink("Add Recall", "Create", "VehicleRecall", new { vehicleId = Model.VehicleId }, new { @class = "btn" }) +
+ @Html.ActionLink("Vehicle Details", "Details", "Vehicle", new { id = Model.VehicleId }, new { @class = "btn" })
\ No newline at end of file diff --git a/Web/Views/Shared/DisplayTemplates/Details.cshtml b/Web/Views/Shared/DisplayTemplates/Details.cshtml new file mode 100644 index 0000000..13f2689 --- /dev/null +++ b/Web/Views/Shared/DisplayTemplates/Details.cshtml @@ -0,0 +1,19 @@ +@model MileageTraker.Web.ViewModels.VehicleRecall.VehicleRecallViewModel + +@{ + ViewBag.Title = "Vehicle Recall Details" ; +} + +@Html.Partial("_StatusMessage") + +

@ViewBag.Title

+
+ @Html.DisplayForModel() +
+ +
+ @Html.ActionLink("Edit", "Edit", new { id = Model.VehicleRecallId }, new { @class = "btn" }) + @Html.ActionLink("Delete", "Delete", new { id = Model.VehicleId }, new { @class = "btn" }) + @Html.ActionLink("Vehicle Details", "Details", "Vehicle", new { id = Model.VehicleId }, new{@class="btn"}) + @Html.ActionLink("Add Service", "Create", "VehicleService", new { VehicleId = Model.VehicleId }, new{@class="btn"}) +
\ No newline at end of file diff --git a/Web/Views/Shared/DisplayTemplates/VehicleRecallSelectListViewModel.cshtml b/Web/Views/Shared/DisplayTemplates/VehicleRecallSelectListViewModel.cshtml new file mode 100644 index 0000000..840b9cd --- /dev/null +++ b/Web/Views/Shared/DisplayTemplates/VehicleRecallSelectListViewModel.cshtml @@ -0,0 +1,12 @@ +@model MileageTraker.Web.ViewModels.SelectListViewModel +@{ + Layout = "~/Views/Shared/DisplayTemplates/_FieldLayout.cshtml"; + if (Model.Selected > 0) + { + var selected = Model.Available.FirstOrDefault(i => i.Value == Model.Selected.ToString()); + if (selected != null) + { + @Html.ActionLink(selected.Text, "Details", "VehicleRecall", new { id = selected.Value }, null) + } + } +} diff --git a/Web/Views/Shared/DisplayTemplates/VehicleServiceLink.cshtml b/Web/Views/Shared/DisplayTemplates/VehicleServiceLink.cshtml new file mode 100644 index 0000000..fd31589 --- /dev/null +++ b/Web/Views/Shared/DisplayTemplates/VehicleServiceLink.cshtml @@ -0,0 +1,7 @@ +@{ + Layout = "~/Views/Shared/DisplayTemplates/_FieldLayout.cshtml"; +} +@if (Model != null) +{ + @Html.ActionLink("View Vehicle Service", "Details", "VehicleService", new { id = Model}, null) +} \ No newline at end of file diff --git a/Web/Views/Shared/EditorTemplates/SelectListViewModel.cshtml b/Web/Views/Shared/EditorTemplates/SelectListViewModel.cshtml index 296ece1..c8ce7e5 100644 --- a/Web/Views/Shared/EditorTemplates/SelectListViewModel.cshtml +++ b/Web/Views/Shared/EditorTemplates/SelectListViewModel.cshtml @@ -1,5 +1,7 @@ @model MileageTraker.Web.ViewModels.SelectListViewModel @{ Layout = "~/Views/Shared/EditorTemplates/_FieldLayout.cshtml"; + var values = ViewData.ModelMetadata.AdditionalValues; + var optionLabel = values.ContainsKey("OptionLabel") ? (string)values["OptionLabel"] : string.Empty; } -@Html.DropDownListFor(m => m.Selected, Model.Available, string.Empty) \ No newline at end of file +@Html.DropDownListFor(m => m.Selected, Model.Available, optionLabel) \ No newline at end of file diff --git a/Web/Views/Shared/EditorTemplates/VehicleRecallIdSelect.cshtml b/Web/Views/Shared/EditorTemplates/VehicleRecallIdSelect.cshtml new file mode 100644 index 0000000..c36f17a --- /dev/null +++ b/Web/Views/Shared/EditorTemplates/VehicleRecallIdSelect.cshtml @@ -0,0 +1,5 @@ +@model MileageTraker.Web.ViewModels.SelectListViewModel +@{ + Layout = "~/Views/Shared/EditorTemplates/_FieldLayout.cshtml"; +} +@Html.DropDownList("", new List(new List{new SelectListItem{Text = "Not a recall", Value = "None"}})) \ No newline at end of file diff --git a/Web/Views/Shared/_Layout.cshtml b/Web/Views/Shared/_Layout.cshtml index 59fde7d..696c8f0 100644 --- a/Web/Views/Shared/_Layout.cshtml +++ b/Web/Views/Shared/_Layout.cshtml @@ -41,6 +41,7 @@ @Html.ActionLink("Logs", "Index", "Log", new { Model.VehicleId}, new { @class = "btn" }) diff --git a/Web/Views/Vehicle/DetailsQuickPartial.cshtml b/Web/Views/Vehicle/DetailsQuickPartial.cshtml new file mode 100644 index 0000000..59827fb --- /dev/null +++ b/Web/Views/Vehicle/DetailsQuickPartial.cshtml @@ -0,0 +1,5 @@ +@model MileageTraker.Web.ViewModels.Vehicle.VehiclePartialDetailsQuick +@{ + Layout = null; +} +@Html.DisplayForModel() \ No newline at end of file diff --git a/Web/Views/VehicleRecall/Create.cshtml b/Web/Views/VehicleRecall/Create.cshtml new file mode 100644 index 0000000..5b6353f --- /dev/null +++ b/Web/Views/VehicleRecall/Create.cshtml @@ -0,0 +1,28 @@ +@model MileageTraker.Web.ViewModels.VehicleRecall.VehicleRecallViewModel + +@{ + ViewBag.Title = "Enter Vehicle Recall"; +} + +@section Scripts { + +} + +@Html.Partial("_StatusMessage") + +

@ViewBag.Title

+

Enter an available recall for vehicle

+ +@using (Html.BeginForm("Create", "VehicleRecall", FormMethod.Post, new { @class = "form-horizontal well center-content" })) +{ + @Html.Partial("_ValidationSummary") +
+ + @Html.EditorForModel() +
+ +
+
+} diff --git a/Web/Views/VehicleRecall/Delete.cshtml b/Web/Views/VehicleRecall/Delete.cshtml new file mode 100644 index 0000000..ec4b0d4 --- /dev/null +++ b/Web/Views/VehicleRecall/Delete.cshtml @@ -0,0 +1,24 @@ +@model MileageTraker.Web.ViewModels.VehicleRecall.VehicleRecallViewModel + +@{ + ViewBag.Title = "Delete Vehicle Recall"; +} + +@Html.Partial("_StatusMessage") + +

@ViewBag.Title

+ +
Are you sure you wish to delete this recall?
+ +
+ + @Html.DisplayForModel() + + @using (Html.BeginForm("Delete", "VehicleRecall", FormMethod.Post, new { @class = "form-horizontal" })) + { +
+ + @Html.ActionLink("Cancel", "Details", new { id = Model.VehicleRecallId }, new { @class = "btn" }) +
+ } +
\ No newline at end of file diff --git a/Web/Views/VehicleRecall/Details.cshtml b/Web/Views/VehicleRecall/Details.cshtml new file mode 100644 index 0000000..e6cc441 --- /dev/null +++ b/Web/Views/VehicleRecall/Details.cshtml @@ -0,0 +1,28 @@ +@using System.Activities.Expressions +@model MileageTraker.Web.ViewModels.VehicleRecall.VehicleRecallViewModel + +@{ + ViewBag.Title = "Vehicle Recall Details" ; + var completed = Model.CompletedService_VehicleServiceId > 0; +} + +@Html.Partial("_StatusMessage") + +

@ViewBag.Title

+
+ @Html.DisplayForModel() + @if (completed) + { + @Html.DisplayFor(v => v.CompletedService_VehicleServiceId) + } +
+@if (!completed) +{ +
Completed? @Html.ActionLink("Add Service", "Create", "VehicleService", new { VehicleId = Model.VehicleId }, new { @class = "btn" })
+} + +
+ @Html.ActionLink("Edit", "Edit", new { id = Model.VehicleRecallId }, new { @class = "btn" }) + @Html.ActionLink("Delete", "Delete", new { id = Model.VehicleId }, new { @class = "btn" }) + @Html.ActionLink("Vehicle Details", "Details", "Vehicle", new { id = Model.VehicleId }, new{@class="btn"}) +
\ No newline at end of file diff --git a/Web/Views/VehicleRecall/Edit.cshtml b/Web/Views/VehicleRecall/Edit.cshtml new file mode 100644 index 0000000..e6c2133 --- /dev/null +++ b/Web/Views/VehicleRecall/Edit.cshtml @@ -0,0 +1,27 @@ +@model MileageTraker.Web.ViewModels.VehicleRecall.VehicleRecallViewModel + +@{ + ViewBag.Title = "Edit Vehicle Recall"; +} + +@section Scripts { + +} + +@Html.Partial("_StatusMessage") + +

@ViewBag.Title

+ +@using (Html.BeginForm("Edit", "VehicleRecall", FormMethod.Post, new { @class = "form-horizontal well center-content" })) +{ + @Html.Partial("_ValidationSummary") +
+ + @Html.EditorForModel() +
+ +
+
+} diff --git a/Web/Views/VehicleRecall/Empty.cshtml b/Web/Views/VehicleRecall/Empty.cshtml new file mode 100644 index 0000000..f94f541 --- /dev/null +++ b/Web/Views/VehicleRecall/Empty.cshtml @@ -0,0 +1,9 @@ +@{ + ViewBag.Title = "No Open Vehicle Recalls"; +} + +

@ViewBag.Title

+ +
+ No open Vehicle Recalls. @Html.ActionLink("Add Recall", "Create", null, new { @class = "btn" }) +
\ No newline at end of file diff --git a/Web/Views/VehicleRecall/Index.cshtml b/Web/Views/VehicleRecall/Index.cshtml new file mode 100644 index 0000000..8379e46 --- /dev/null +++ b/Web/Views/VehicleRecall/Index.cshtml @@ -0,0 +1,30 @@ +@model IEnumerable + +@{ + ViewBag.Title = "Vehicle Recalls"; + var grid = new WebGrid(Model, rowsPerPage: 45); +} + +@Html.Partial("_StatusMessage") + +

@ViewBag.Title

+ +
+ @Html.ActionLink("Add Recall", "Create", null, new { @class = "btn" }) + Open Recalls @Model.Count() +
+ +@grid.GetHtml(columns: + grid.Columns( + grid.Column("VehicleId", "Vehicle ID", item => Html.ActionLink((string)item.VehicleId, "DetailsPartial", "Vehicle", new { id = item.VehicleId }, new { @class = "qtip-modal" })), + grid.Column("Identifier", "Identifier", item => item.Identifier), + grid.Column("Description", "Description"), + grid.Column(format: + @
+ @Html.ActionLink("Details", "Details", new { id = item.VehicleRecallId }, new { @class = "btn btn-mini" }) + @Html.ActionLink("Delete", "Delete", new { id = item.VehicleRecallId }, new { @class = "btn btn-mini" }) +
) + ), + htmlAttributes: new { @class = "table table-striped table-bordered table-hover table-condensed"}, + numericLinksCount: 20 + ) \ No newline at end of file diff --git a/Web/Web.csproj b/Web/Web.csproj index 2872e1e..ac65b10 100644 --- a/Web/Web.csproj +++ b/Web/Web.csproj @@ -156,17 +156,25 @@ + + + + + 202008300135173_VehicleRecall.cs + + + @@ -293,6 +301,7 @@ + @@ -390,6 +399,17 @@ + + + + + + + + + + + @@ -628,6 +648,9 @@ 201510150220550_ServiceReminder.cs + + 202008300135173_VehicleRecall.cs + 10.0