Updated edit action to allow add/remove of many to many relationships
parent
3ae5971122
commit
476b2c7f2f
|
@ -245,56 +245,16 @@ namespace LANCommander.Controllers
|
|||
var game = await GameService.Add(viewModel.Game);
|
||||
|
||||
if (viewModel.SelectedDevelopers != null && viewModel.SelectedDevelopers.Length > 0)
|
||||
{
|
||||
if (game.Developers == null)
|
||||
game.Developers = new List<Company>();
|
||||
|
||||
foreach (var selectedDeveloper in viewModel.SelectedDevelopers)
|
||||
{
|
||||
var company = await CompanyService.AddMissing(c => c.Name == selectedDeveloper, new Company() { Name = selectedDeveloper });
|
||||
|
||||
game.Developers.Add(company);
|
||||
}
|
||||
}
|
||||
game.Developers = viewModel.SelectedDevelopers.Select(async d => await CompanyService.AddMissing(x => x.Name == d, new Company() { Name = d })).Select(t => t.Result).ToList();
|
||||
|
||||
if (viewModel.SelectedPublishers != null && viewModel.SelectedPublishers.Length > 0)
|
||||
{
|
||||
if (game.Publishers == null)
|
||||
game.Publishers = new List<Company>();
|
||||
|
||||
foreach (var selectedPublisher in viewModel.SelectedPublishers)
|
||||
{
|
||||
var company = await CompanyService.AddMissing(c => c.Name == selectedPublisher, new Company() { Name = selectedPublisher });
|
||||
|
||||
game.Publishers.Add(company);
|
||||
}
|
||||
}
|
||||
game.Publishers = viewModel.SelectedPublishers.Select(async p => await CompanyService.AddMissing(x => x.Name == p, new Company() { Name = p })).Select(t => t.Result).ToList();
|
||||
|
||||
if (viewModel.SelectedGenres != null && viewModel.SelectedGenres.Length > 0)
|
||||
{
|
||||
if (game.Genres == null)
|
||||
game.Genres = new List<Genre>();
|
||||
|
||||
foreach (var selectedGenre in viewModel.SelectedGenres)
|
||||
{
|
||||
var genre = await GenreService.AddMissing(g => g.Name == selectedGenre, new Genre() { Name = selectedGenre });
|
||||
|
||||
game.Genres.Add(genre);
|
||||
}
|
||||
}
|
||||
game.Genres = viewModel.SelectedGenres.Select(async g => await GenreService.AddMissing(x => x.Name == g, new Genre() { Name = g })).Select(t => t.Result).ToList();
|
||||
|
||||
if (viewModel.SelectedTags != null && viewModel.SelectedTags.Length > 0)
|
||||
{
|
||||
if (game.Tags == null)
|
||||
game.Tags = new List<Tag>();
|
||||
|
||||
foreach (var selectedTag in viewModel.SelectedTags)
|
||||
{
|
||||
var tag = await TagService.AddMissing(g => g.Name == selectedTag, new Tag() { Name = selectedTag });
|
||||
|
||||
game.Tags.Add(tag);
|
||||
}
|
||||
}
|
||||
game.Tags = viewModel.SelectedTags.Select(async t => await TagService.AddMissing(x => x.Name == t, new Tag() { Name = t })).Select(t => t.Result).ToList();
|
||||
|
||||
await GameService.Update(game);
|
||||
|
||||
|
@ -307,12 +267,34 @@ namespace LANCommander.Controllers
|
|||
// GET: Games/Edit/5
|
||||
public async Task<IActionResult> Edit(Guid? id)
|
||||
{
|
||||
Game game = await GameService.Get(id.GetValueOrDefault());
|
||||
var viewModel = new GameViewModel();
|
||||
|
||||
if (game == null)
|
||||
viewModel.Game = await GameService.Get(id.GetValueOrDefault());
|
||||
|
||||
if (viewModel.Game == null)
|
||||
return NotFound();
|
||||
|
||||
return View(game);
|
||||
viewModel.Developers = CompanyService.Get()
|
||||
.OrderBy(c => c.Name)
|
||||
.Select(c => new SelectListItem() { Text = c.Name, Value = c.Name, Selected = viewModel.Game.Developers.Any(d => d.Id == c.Id) })
|
||||
.ToList();
|
||||
|
||||
viewModel.Publishers = CompanyService.Get()
|
||||
.OrderBy(c => c.Name)
|
||||
.Select(c => new SelectListItem() { Text = c.Name, Value = c.Name, Selected = viewModel.Game.Publishers.Any(d => d.Id == c.Id) })
|
||||
.ToList();
|
||||
|
||||
viewModel.Genres = GenreService.Get()
|
||||
.OrderBy(g => g.Name)
|
||||
.Select(g => new SelectListItem() { Text = g.Name, Value = g.Name, Selected = viewModel.Game.Genres.Any(x => x.Id == g.Id) })
|
||||
.ToList();
|
||||
|
||||
viewModel.Tags = TagService.Get()
|
||||
.OrderBy(t => t.Name)
|
||||
.Select(t => new SelectListItem() { Text = t.Name, Value = t.Name, Selected = viewModel.Game.Tags.Any(x => x.Id == t.Id) })
|
||||
.ToList();
|
||||
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
// POST: Games/Edit/5
|
||||
|
@ -320,34 +302,91 @@ namespace LANCommander.Controllers
|
|||
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Edit(Guid id, Game game)
|
||||
public async Task<IActionResult> Edit(Guid id, GameViewModel viewModel)
|
||||
{
|
||||
if (id != game.Id)
|
||||
if (id != viewModel.Game.Id)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
try
|
||||
var game = await GameService.Get(viewModel.Game.Id);
|
||||
|
||||
game.Title = viewModel.Game.Title;
|
||||
game.Description = viewModel.Game.Description;
|
||||
game.ReleasedOn = viewModel.Game.ReleasedOn;
|
||||
|
||||
#region Update Developers
|
||||
foreach (var developer in game.Developers)
|
||||
{
|
||||
await GameService.Update(game);
|
||||
if (!viewModel.SelectedDevelopers.Any(d => d == developer.Name))
|
||||
game.Developers.Remove(developer);
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
|
||||
foreach (var newDeveloper in viewModel.SelectedDevelopers.Where(sd => !game.Developers.Any(d => d.Name == sd)))
|
||||
{
|
||||
if (!GameService.Exists(game.Id))
|
||||
game.Developers.Add(new Company()
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
Name = newDeveloper
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Update Publishers
|
||||
foreach (var publisher in game.Publishers)
|
||||
{
|
||||
if (!viewModel.SelectedPublishers.Any(p => p == publisher.Name))
|
||||
game.Publishers.Remove(publisher);
|
||||
}
|
||||
|
||||
foreach (var newPublisher in viewModel.SelectedPublishers.Where(sp => !game.Publishers.Any(p => p.Name == sp)))
|
||||
{
|
||||
game.Publishers.Add(new Company()
|
||||
{
|
||||
Name = newPublisher
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Update Genres
|
||||
foreach (var genre in game.Genres)
|
||||
{
|
||||
if (!viewModel.SelectedGenres.Any(g => g == genre.Name))
|
||||
game.Genres.Remove(genre);
|
||||
}
|
||||
|
||||
foreach (var newGenre in viewModel.SelectedGenres.Where(sg => !game.Genres.Any(g => g.Name == sg)))
|
||||
{
|
||||
game.Genres.Add(new Genre()
|
||||
{
|
||||
Name = newGenre
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Update Tags
|
||||
foreach (var tag in game.Tags)
|
||||
{
|
||||
if (!viewModel.SelectedTags.Any(t => t == tag.Name))
|
||||
game.Tags.Remove(tag);
|
||||
}
|
||||
|
||||
foreach (var newTag in viewModel.SelectedTags.Where(st => !game.Tags.Any(t => t.Name == st)))
|
||||
{
|
||||
game.Tags.Add(new Tag()
|
||||
{
|
||||
Name = newTag
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
|
||||
await GameService.Update(game);
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
return View(game);
|
||||
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
// GET: Games/Delete/5
|
||||
|
|
|
@ -82,6 +82,8 @@ namespace LANCommander.Services
|
|||
{
|
||||
using (var repo = new Repository<T>(Context, HttpContext))
|
||||
{
|
||||
Context.Attach(entity);
|
||||
|
||||
entity = repo.Update(entity);
|
||||
await repo.SaveChanges();
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@using LANCommander.Data.Models
|
||||
@model LANCommander.Data.Models.Game
|
||||
@model LANCommander.Models.GameViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Edit";
|
||||
|
@ -29,27 +29,52 @@
|
|||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label asp-for="Title" class="control-label"></label>
|
||||
<input asp-for="Title" class="form-control" />
|
||||
<span asp-validation-for="Title" class="text-danger"></span>
|
||||
<label asp-for="Game.Title" class="control-label"></label>
|
||||
<div class="input-group">
|
||||
<input asp-for="Game.Title" class="form-control" />
|
||||
<button class="btn" type="submit" asp-action="Lookup">Lookup</button>
|
||||
</div>
|
||||
<span asp-validation-for="Game.Title" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label asp-for="SortTitle" class="control-label"></label>
|
||||
<input asp-for="SortTitle" class="form-control" />
|
||||
<span asp-validation-for="SortTitle" class="text-danger"></span>
|
||||
<label asp-for="Game.SortTitle" class="control-label"></label>
|
||||
<input asp-for="Game.SortTitle" class="form-control" />
|
||||
<span asp-validation-for="Game.SortTitle" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label asp-for="Description" class="control-label"></label>
|
||||
<input asp-for="Description" class="form-control" />
|
||||
<span asp-validation-for="Description" class="text-danger"></span>
|
||||
<label asp-for="Game.Description" class="control-label"></label>
|
||||
<textarea asp-for="Game.Description" class="form-control" data-bs-toggle="autosize"></textarea>
|
||||
<span asp-validation-for="Game.Description" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label asp-for="ReleasedOn" class="control-label"></label>
|
||||
<input asp-for="ReleasedOn" class="form-control" />
|
||||
<span asp-validation-for="ReleasedOn" class="text-danger"></span>
|
||||
<label asp-for="Game.ReleasedOn" class="control-label"></label>
|
||||
<input asp-for="Game.ReleasedOn" class="form-control" />
|
||||
<span asp-validation-for="Game.ReleasedOn" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label asp-for="Developers" class="control-label"></label>
|
||||
<input type="text" class="developer-select" />
|
||||
<select asp-for="SelectedDevelopers" class="d-none"></select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label asp-for="Publishers" class="control-label"></label>
|
||||
<input type="text" class="publisher-select" />
|
||||
<select asp-for="SelectedPublishers" class="d-none"></select>
|
||||
</div>
|
||||
|
||||
<input type="hidden" asp-for="Id" />
|
||||
<div class="mb-3">
|
||||
<label asp-for="Genres" class="control-label"></label>
|
||||
<input type="text" class="genre-select" />
|
||||
<select asp-for="SelectedGenres" class="d-none"></select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label asp-for="Tags" class="control-label"></label>
|
||||
<input type="text" class="tag-select" />
|
||||
<select asp-for="SelectedTags" class="d-none"></select>
|
||||
</div>
|
||||
|
||||
<input type="hidden" asp-for="Game.Id" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -65,18 +90,18 @@
|
|||
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
@if (Model.Keys != null && Model.Keys.Count > 0)
|
||||
@if (Model.Game.Keys != null && Model.Game.Keys.Count > 0)
|
||||
{
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Keys</h3>
|
||||
<div class="card-actions">
|
||||
<a asp-action="Details" asp-controller="Keys" asp-route-id="@Model.Id" class="btn btn-ghost-primary">
|
||||
<a asp-action="Details" asp-controller="Keys" asp-route-id="@Model.Game.Id" class="btn btn-ghost-primary">
|
||||
Details
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
var keysAvailable = Model.Keys.Count(k =>
|
||||
var keysAvailable = Model.Game.Keys.Count(k =>
|
||||
{
|
||||
return (k.AllocationMethod == KeyAllocationMethod.MacAddress && String.IsNullOrWhiteSpace(k.ClaimedByMacAddress)) ||
|
||||
(k.AllocationMethod == KeyAllocationMethod.UserAccount && k.ClaimedByUser == null);
|
||||
|
@ -93,13 +118,13 @@
|
|||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">Claimed</div>
|
||||
<div class="datagrid-content">
|
||||
@(Model.Keys.Count - keysAvailable)
|
||||
@(Model.Game.Keys.Count - keysAvailable)
|
||||
</div>
|
||||
</div>
|
||||
<div class="datagrid-item">
|
||||
<div class="datagrid-title">Total</div>
|
||||
<div class="datagrid-content">
|
||||
@Model.Keys.Count
|
||||
@Model.Game.Keys.Count
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -111,7 +136,7 @@
|
|||
<p class="empty-title">No Keys</p>
|
||||
<p class="empty-subtitle text-muted">There have been no keys added for this game.</p>
|
||||
<div class="empty-action">
|
||||
<a asp-action="Edit" asp-controller="Keys" asp-route-id="@Model.Id" class="btn btn-primary">Edit Keys</a>
|
||||
<a asp-action="Edit" asp-controller="Keys" asp-route-id="@Model.Game.Id" class="btn btn-primary">Edit Keys</a>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
@ -120,12 +145,12 @@
|
|||
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
@if (Model.Archives != null && Model.Archives.Count > 0)
|
||||
@if (Model.Game.Archives != null && Model.Game.Archives.Count > 0)
|
||||
{
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Archives</h3>
|
||||
<div class="card-actions">
|
||||
<a asp-action="Add" asp-controller="Archives" asp-route-id="@Model.Id" class="btn btn-primary">
|
||||
<a asp-action="Add" asp-controller="Archives" asp-route-id="@Model.Game.Id" class="btn btn-primary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
|
||||
Add
|
||||
</a>
|
||||
|
@ -145,7 +170,7 @@
|
|||
</thead>
|
||||
|
||||
<tbody>
|
||||
@foreach (var archive in Model.Archives.OrderByDescending(a => a.CreatedOn))
|
||||
@foreach (var archive in Model.Game.Archives.OrderByDescending(a => a.CreatedOn))
|
||||
{
|
||||
<tr>
|
||||
<td>@Html.DisplayFor(m => archive.Version)</td>
|
||||
|
@ -170,7 +195,7 @@
|
|||
<p class="empty-title">No Archives</p>
|
||||
<p class="empty-subtitle text-muted">There have been no archives uploaded for this game.</p>
|
||||
<div class="empty-action">
|
||||
<a asp-action="Add" asp-controller="Archives" asp-route-id="@Model.Id" class="btn btn-primary">Upload Archive</a>
|
||||
<a asp-action="Add" asp-controller="Archives" asp-route-id="@Model.Game.Id" class="btn btn-primary">Upload Archive</a>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
@ -182,4 +207,61 @@
|
|||
|
||||
@section Scripts {
|
||||
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||
|
||||
<script>
|
||||
var developers = @Html.Raw(Json.Serialize(Model.Developers));
|
||||
var publishers = @Html.Raw(Json.Serialize(Model.Publishers));
|
||||
var genres = @Html.Raw(Json.Serialize(Model.Genres));
|
||||
var tags = @Html.Raw(Json.Serialize(Model.Tags));
|
||||
|
||||
function selectize(selector, options) {
|
||||
var selected = [];
|
||||
|
||||
for (let option of options) {
|
||||
if (option.selected)
|
||||
selected.push(option.value);
|
||||
}
|
||||
|
||||
$(selector).val(selected.join('|'));
|
||||
|
||||
$(selector).selectize({
|
||||
delimiter: '|',
|
||||
plugins: ['remove_button'],
|
||||
create: true,
|
||||
valueField: 'value',
|
||||
labelField: 'text',
|
||||
searchField: 'text',
|
||||
options: options,
|
||||
onItemAdd: function(value) {
|
||||
for (let option of Object.values(this.options)) {
|
||||
if (option.value == value)
|
||||
{
|
||||
this.$input.siblings('select').append(`<option value="${option.value}" selected>${option.text}</option>`);
|
||||
}
|
||||
}
|
||||
},
|
||||
onItemRemove: function(value) {
|
||||
for (let option of Object.values(this.options)) {
|
||||
if (option.value == value)
|
||||
{
|
||||
this.$input.siblings('select').find(`option[value="${option.value}"]`).remove();
|
||||
}
|
||||
}
|
||||
},
|
||||
onInitialize: function() {
|
||||
for (let option of Object.values(this.options)) {
|
||||
if (option.selected)
|
||||
{
|
||||
this.$input.siblings('select').append(`<option value="${option.value}" selected>${option.text}</option>`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
selectize('.developer-select', developers);
|
||||
selectize('.publisher-select', publishers);
|
||||
selectize('.genre-select', genres);
|
||||
selectize('.tag-select', tags);
|
||||
</script>
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue