using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.EntityFrameworkCore; using ThAmCo.Events.Data; using ThAmCo.Events.Models; using System.Net.Http; using System.Diagnostics; using ThAmCo.Events.Models.ViewModels.Events; using ThAmCo.Events.Models.Dto; namespace ThAmCo.Events.Views { public class EventsController : Controller { private readonly EventsDbContext _context; public EventsController(EventsDbContext context) { _context = context; } // GET: Events public async Task Index() { var @event = await _context.Events.Include(e => e.Bookings).Include(s => s.Staffings).ThenInclude(s => s.Staff).Where(e => e.IsDeleted == false).ToListAsync(); return View(@event); } // GET: Events/Details/5 public async Task Details(int? id) { if (id == null) { return NotFound(); } var @event = await _context.Events .FirstOrDefaultAsync(m => m.Id == id); //Get both bookings and staffings from the database so pass to the view, so it can show the guests and staff for that event. @event.Bookings = await _context.Guests.Include(m => m.Customer).Where(m => m.EventId == id).ToListAsync(); @event.Staffings = await _context.Staffing.Include(m => m.Staff).Where(m => m.EventId == id).ToListAsync(); if (@event == null) { return NotFound(); } ReservationDto reservation = null; //Get the reservation information for the event, if it exists. HttpClient client = new HttpClient { BaseAddress = new Uri("http://localhost:23652/") }; client.DefaultRequestHeaders.Accept.ParseAdd("application/json"); HttpResponseMessage response = await client.GetAsync("api/reservations/" + @event.VenueReference); if (response.IsSuccessStatusCode) { reservation = await response.Content.ReadAsAsync(); } else { Debug.WriteLine("Index received a bad response from the web service."); } var edm = new EventDetailsModel { Id = @event.Id, Title = @event.Title, Date = @event.Date, Duration = @event.Duration, TypeId = @event.TypeId, Bookings = @event.Bookings, Reservation = reservation, FoodReference = @event.FoodReference, Staffing = @event.Staffings }; return View(edm); } // GET: Events/Create public async Task Create() { //For this code, AJAX calls can be used, along with regular HTTP redirects. //It depends on whether the programmer wants the client or the server to make the call to the web service. //This isn't a live action effecting the user interface, so it can be done either way. //This code works and returns the correct things, however the ViewBag doesn't have the correct ID. /*var eventTypes = new List().AsEnumerable(); HttpClient client = new HttpClient { BaseAddress = new Uri("http://localhost:23652/") }; client.DefaultRequestHeaders.Accept.ParseAdd("application/json"); HttpResponseMessage response = await client.GetAsync("api/eventtypes"); if (response.IsSuccessStatusCode) { eventTypes = await response.Content.ReadAsAsync>(); } else { Debug.WriteLine("Index received a bad response from the web service."); } ViewData["EventLists"] = new SelectList(eventTypes, "TypeId", "Title");*/ return View(); } // POST: Events/Create // To protect from overposting attacks, please enable the specific properties you want to bind to, for // more details see http://go.microsoft.com/fwlink/?LinkId=317598. [HttpPost] [ValidateAntiForgeryToken] public async Task Create([Bind("Id,Title,Date,Duration,TypeId")] Event @event) { if (ModelState.IsValid) { _context.Add(@event); await _context.SaveChangesAsync(); //Return to the newly created Details page of the event. return RedirectToAction(nameof(Details), new { id = @event.Id }); } return View(@event); } // GET: Events/SelectVenue public async Task SelectVenue(int id) { if (ModelState.IsValid) { var @event = _context.Events.FirstOrDefaultAsync(e => e.Id == id).Result; //Check if event exists. if (@event.Id != 0) { var venues = new List().AsEnumerable(); //For the Selection of the Venue, it could all be handled within one page, however it's loaded on page load. //AJAX could be used here also, however loading from the server is also fine. //Get list of the available venues from the web service. HttpClient client = new HttpClient { BaseAddress = new Uri("http://localhost:23652/") }; client.DefaultRequestHeaders.Accept.ParseAdd("application/json"); HttpResponseMessage response = await client.GetAsync("api/Availability?eventType=" + @event.TypeId + "&beginDate=" + @event.Date.ToString("MM/dd/yyyy") + "&endDate=" + @event.Date.ToString("MM/dd/yyyy")); if (response.IsSuccessStatusCode) { venues = await response.Content.ReadAsAsync>(); } else { Debug.WriteLine("Index received a bad response from the web service."); } SelectVenueModel svm = new SelectVenueModel { Id = id, Venues = venues.ToList() }; return View(@svm); } } return RedirectToAction(nameof(Details), new { id = id }); } [HttpPost] [ValidateAntiForgeryToken] public async Task CreateReservation([Bind("Id,VenueDate,VenueCode")] EventCreateReservationModel @event) { if (ModelState.IsValid) { //Add reservation (call add api) //Needs redirect afterwards so AJAX doesn't need to be used to call here. HttpClient client = new HttpClient() { BaseAddress = new Uri("http://localhost:23652/") }; client.DefaultRequestHeaders.Accept.ParseAdd("application/json"); var reservation = new ReservationCreateDto { EventDate = @event.VenueDate, VenueCode = @event.VenueCode, StaffId = "Test" }; HttpResponseMessage response = await client.PostAsJsonAsync("api/reservations", reservation); //Add the venue reference to the database. var eventContext = await _context.Events.Include(m => m.Bookings).FirstOrDefaultAsync(e => e.Id == @event.Id); var originalEvent = eventContext; eventContext.VenueReference = @event.VenueCode + @event.VenueDate.ToString("yyyyMMdd"); _context.Entry(originalEvent).CurrentValues.SetValues(eventContext); _context.SaveChanges(); return RedirectToAction(nameof(Details), new { id = @event.Id }); } return View(@event); } public async Task FreeReservation(int id) { var eventContext = await _context.Events.FirstOrDefaultAsync(e => e.Id == id); if (eventContext.Id != 0) { //Request that the reservation called is deleted by the web service. HttpClient client = new HttpClient() { BaseAddress = new Uri("http://localhost:23652/") }; client.DefaultRequestHeaders.Accept.ParseAdd("application/json"); HttpResponseMessage response = await client.DeleteAsync("api/reservations/" + eventContext.VenueReference); if (response.IsSuccessStatusCode) { //Save this change to local database. var editedEventContext = eventContext; editedEventContext.VenueReference = null; _context.Entry(eventContext).CurrentValues.SetValues(editedEventContext); _context.SaveChanges(); return RedirectToAction(nameof(Details), new { id = id }); } else { Debug.WriteLine("Index Delete Error."); } } return RedirectToAction(nameof(Details), new { id = id }); } // GET: Events/Edit/5 public async Task Edit(int? id) { if (id == null) { return NotFound(); } var @event = await _context.Events.FindAsync(id); if (@event == null) { return NotFound(); } return View(@event); } // POST: Events/Edit/5 // To protect from overposting attacks, please enable the specific properties you want to bind to, for // more details see http://go.microsoft.com/fwlink/?LinkId=317598. [HttpPost] [ValidateAntiForgeryToken] public async Task Edit(int id, [Bind("Id,Title,Duration")] EventEditModel @event) { var eventContext = await _context.Events.FirstOrDefaultAsync(m => m.Id == id); eventContext.Title = @event.Title; eventContext.Duration = @event.Duration; if (id != @event.Id) { return NotFound(); } if (ModelState.IsValid) { try { _context.Update(eventContext); await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!EventExists(@event.Id)) { return NotFound(); } else { throw; } } return RedirectToAction(nameof(Index)); } return View(@event); } //Register attendance for customer at event public async Task RegisterAttendance([Bind("EventId,CustomerId,Attending")] EventRegisterAttendanceModel @event) { //Change attendance for a customer at an event (toggle) if (@event.CustomerId != 0 && @event.EventId != 0) { if (_context.Guests.Any(g => g.EventId == @event.EventId && g.CustomerId == @event.CustomerId)) { var booking = await _context.Guests.FirstOrDefaultAsync(g => g.EventId == @event.EventId && g.CustomerId == @event.CustomerId); booking.Attended = !booking.Attended; _context.Update(booking); await _context.SaveChangesAsync(); } } return RedirectToAction(nameof(Details), new { id = @event.EventId }); } // GET: Events/Delete/5 public async Task Delete(int? id) { if (id == null) { return NotFound(); } var @event = await _context.Events .FirstOrDefaultAsync(m => m.Id == id); if (@event == null) { return NotFound(); } return View(@event); } // POST: Events/Delete/5 [HttpPost, ActionName("Delete")] [ValidateAntiForgeryToken] public async Task DeleteConfirmed(int id) { //Change the event to deleted. var @event = await _context.Events.FindAsync(id); if (@event != null) { //Ask web api to delete the reservation that the event used to hold. HttpClient client = new HttpClient() { BaseAddress = new Uri("http://localhost:23652/") }; client.DefaultRequestHeaders.Accept.ParseAdd("application/json"); HttpResponseMessage response = await client.DeleteAsync("api/reservations/" + @event.VenueReference); //Soft deletion is done by having a boolean value in the object, true being deleted and false being not deleted. //Other ways of doing this could be nullable values, such as dates, such that not null values are not deleted, and null values are deleted. //Using dates is useful if you want to un-delete a lot of data at once from a certain time. //If successful, soft delete event. @event.IsDeleted = true; @event.VenueReference = null; _context.Events.Update(@event); /*Guest bookings can be deleted if required. var guestBookings = await _context.Guests.Where(g => g.EventId == id).ToListAsync(); _context.Guests.RemoveRange(guestBookings);*/ //Hard delete staffings. var staffings = await _context.Staffing.Where(s => s.EventId == id).ToListAsync(); _context.Staffing.RemoveRange(staffings); await _context.SaveChangesAsync(); } return RedirectToAction(nameof(Index)); } private bool EventExists(int id) { return _context.Events.Any(e => e.Id == id); } //Removes a Customer from an Event. // POST: Events/RemoveCustomer/5 [HttpPost] [ValidateAntiForgeryToken] public async Task RemoveCustomer(int id, [Bind("CustomerId,EventId")] GuestBooking guestBooking) { if (id != guestBooking.EventId) { return NotFound(); } if (ModelState.IsValid) { try { _context.Remove(guestBooking); await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { throw; } return RedirectToAction(nameof(Details), new { id }); } return RedirectToAction(nameof(Details)); } public async Task RemoveStaff(int id, [Bind("StaffId,EventId")] Staffing staffing) { if (id != staffing.EventId) { return NotFound(); } if (ModelState.IsValid && staffing.EventId != 0 && staffing.StaffId != 0) { try { _context.Remove(staffing); await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { throw; } return RedirectToAction(nameof(Details), new { id = id }); } return RedirectToAction(nameof(Details)); } public async Task AddCustomer(int id) { //Get customers that dont exist in current event. var guests = await _context.Guests.Include(g => g.Customer).Where(g => g.EventId == id).Select(c => c.Customer).ToListAsync(); var customers = await _context.Customers.ToListAsync(); //This seems to be the simplest way to go about it in LINQ. //Excludes data in the current set. var result = await _context.Customers.Except(guests).ToListAsync(); EventAddCustomerModel eacm = new EventAddCustomerModel { EventId = id, Customer = result }; return View(eacm); } [HttpPost] [ValidateAntiForgeryToken] public async Task AddCustomer([Bind("CustomerId,EventId")] GuestBooking guestBooking) { if (ModelState.IsValid) { guestBooking.Customer = _context.Customers.First(c => c.Id == guestBooking.CustomerId); guestBooking.Event = _context.Events.First(e => e.Id == guestBooking.EventId); _context.Add(guestBooking); await _context.SaveChangesAsync(); //Return to the newly created Details page of the event. return RedirectToAction(nameof(Details), new { id = guestBooking.EventId }); } return RedirectToAction(nameof(Details)); } public async Task AddStaff(int id) { var staff = await _context.Staffing.Include(g => g.Staff).Where(g => g.EventId == id).Select(c => c.Staff).ToListAsync(); //Also uses the except function in LINQ. var result = await _context.Staff.Except(staff).ToListAsync(); EventAddStaffModel eacm = new EventAddStaffModel { EventId = id, Staff = result }; return View(eacm); } [HttpPost] [ValidateAntiForgeryToken] public async Task AddStaff([Bind("StaffId,EventId")] Staffing staffing) { if (ModelState.IsValid) { staffing.Staff = _context.Staff.First(s => s.Id == staffing.StaffId); staffing.Event = _context.Events.First(e => e.Id == staffing.EventId); _context.Add(staffing); await _context.SaveChangesAsync(); //Return to the newly created Details page of the event. return RedirectToAction(nameof(Details), new { id = staffing.EventId }); } return RedirectToAction(nameof(Details)); } public async Task AddMenu(int id) { if (EventExists(id)) { //Get menu back from api. HttpClient client = new HttpClient() { BaseAddress = new Uri("http://localhost:32824") }; client.DefaultRequestHeaders.Accept.ParseAdd("application/json"); HttpResponseMessage response = await client.GetAsync("api/values"); if (response.IsSuccessStatusCode) { var menus = response.Content.ReadAsAsync>().Result.ToList(); EventAddMenuModel eamm = new EventAddMenuModel { EventId = id, Menus = menus }; return View(eamm); } else { Debug.WriteLine("API GET Error."); } } return RedirectToAction(nameof(Details), new { id = id }); } [HttpPost] [ValidateAntiForgeryToken] public async Task AddMenu(int EventID, int MenuID) { if (EventExists(EventID)) { //Add the menu reference to database. var @event = _context.Events.First(e => e.Id == EventID); @event.FoodReference = MenuID; _context.Update(@event); await _context.SaveChangesAsync(); return RedirectToAction(nameof(Details), new { id = EventID }); } return NotFound(); } public IActionResult FindVenue() { return View(); } [HttpPost] [ValidateAntiForgeryToken] public async Task FindVenueResults([Bind("EventType,StartDate,EndDate")] EventFindVenueModel search) { if (ModelState.IsValid) { //Return all venues that satify the constraints. //AJAX could be used here if it was loading on the same page, however different pages are used. var venues = new List().AsEnumerable(); HttpClient client = new HttpClient { BaseAddress = new Uri("http://localhost:23652/") }; client.DefaultRequestHeaders.Accept.ParseAdd("application/json"); HttpResponseMessage response = await client.GetAsync("api/Availability?eventType=" + search.EventType + "&beginDate=" + search.StartDate.ToString("MM/dd/yyyy") + "&endDate=" + search.EndDate.ToString("MM/dd/yyyy")); if (response.IsSuccessStatusCode) { venues = await response.Content.ReadAsAsync>(); } else { Debug.WriteLine("Index received a bad response from the web service."); } return View(venues); } return NotFound(); } public IActionResult CreateWithVenue(string reference) { return View(new Event { VenueReference = reference }); } } }