using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using NoticeBoard.Data; using NoticeBoard.Models; namespace NoticeBoard.Controllers; public class DevicesController : Controller { private readonly AppDbContext _db; public DevicesController(AppDbContext db) { _db = db; } public async Task Index() { var devices = await _db.Devices .Include(d => d.DeviceSlides) .OrderBy(d => d.Name) .ToListAsync(); return View(devices); } public IActionResult Create() { return View(new Device()); } [HttpPost] [ValidateAntiForgeryToken] public async Task Create(Device device) { if (string.IsNullOrWhiteSpace(device.Slug)) device.Slug = device.Name.ToLower().Replace(" ", "").Replace("-", ""); device.Slug = device.Slug.ToLower().Trim(); if (await _db.Devices.AnyAsync(d => d.Slug == device.Slug)) { ModelState.AddModelError("Slug", "This slug is already in use."); return View(device); } if (!ModelState.IsValid) return View(device); device.CreatedAt = DateTime.UtcNow; _db.Devices.Add(device); await _db.SaveChangesAsync(); TempData["Success"] = $"Device '{device.Name}' created. URL: /{device.Slug}"; return RedirectToAction(nameof(Index)); } public async Task Edit(int id) { var device = await _db.Devices.FindAsync(id); if (device == null) return NotFound(); return View(device); } [HttpPost] [ValidateAntiForgeryToken] public async Task Edit(int id, Device device) { if (id != device.Id) return NotFound(); device.Slug = device.Slug.ToLower().Trim(); if (await _db.Devices.AnyAsync(d => d.Slug == device.Slug && d.Id != id)) { ModelState.AddModelError("Slug", "This slug is already in use."); return View(device); } if (!ModelState.IsValid) return View(device); var existing = await _db.Devices.FindAsync(id); if (existing == null) return NotFound(); existing.Name = device.Name; existing.Slug = device.Slug; existing.ResolutionWidth = device.ResolutionWidth; existing.ResolutionHeight = device.ResolutionHeight; existing.Transition = device.Transition; await _db.SaveChangesAsync(); TempData["Success"] = $"Device '{device.Name}' updated."; return RedirectToAction(nameof(Index)); } public async Task Delete(int id) { var device = await _db.Devices .Include(d => d.DeviceSlides) .FirstOrDefaultAsync(d => d.Id == id); if (device == null) return NotFound(); return View(device); } [HttpPost, ActionName("Delete")] [ValidateAntiForgeryToken] public async Task DeleteConfirmed(int id) { var device = await _db.Devices.FindAsync(id); if (device == null) return NotFound(); _db.Devices.Remove(device); await _db.SaveChangesAsync(); TempData["Success"] = $"Device '{device.Name}' deleted."; return RedirectToAction(nameof(Index)); } // === Playlist Management === public async Task Playlist(int id) { var device = await _db.Devices .Include(d => d.DeviceSlides.OrderBy(ds => ds.DisplayOrder)) .ThenInclude(ds => ds.Slide) .FirstOrDefaultAsync(d => d.Id == id); if (device == null) return NotFound(); var assignedSlideIds = device.DeviceSlides.Select(ds => ds.SlideId).ToHashSet(); ViewBag.AvailableSlides = await _db.Slides .Where(s => !assignedSlideIds.Contains(s.Id)) .OrderBy(s => s.Name) .ToListAsync(); return View(device); } [HttpPost] public async Task AddToPlaylist(int id, int slideId, int durationSeconds = 30) { var device = await _db.Devices.Include(d => d.DeviceSlides).FirstOrDefaultAsync(d => d.Id == id); if (device == null) return NotFound(); var maxOrder = device.DeviceSlides.Any() ? device.DeviceSlides.Max(ds => ds.DisplayOrder) : 0; var ds = new DeviceSlide { DeviceId = id, SlideId = slideId, DisplayOrder = maxOrder + 1, DurationSeconds = durationSeconds, Enabled = true }; _db.DeviceSlides.Add(ds); await _db.SaveChangesAsync(); return RedirectToAction(nameof(Playlist), new { id }); } [HttpPost] public async Task RemoveFromPlaylist(int id, int deviceSlideId) { var ds = await _db.DeviceSlides.FindAsync(deviceSlideId); if (ds == null) return NotFound(); _db.DeviceSlides.Remove(ds); await _db.SaveChangesAsync(); return RedirectToAction(nameof(Playlist), new { id }); } [HttpPost] public async Task UpdatePlaylist(int id, int[] slideIds, int[] durations, bool[] enabled) { var deviceSlides = await _db.DeviceSlides .Where(ds => ds.DeviceId == id) .ToListAsync(); for (int i = 0; i < slideIds.Length; i++) { var ds = deviceSlides.FirstOrDefault(x => x.Id == slideIds[i]); if (ds != null) { ds.DisplayOrder = i + 1; if (i < durations.Length) ds.DurationSeconds = durations[i]; ds.Enabled = i < enabled.Length && enabled[i]; } } await _db.SaveChangesAsync(); TempData["Success"] = "Playlist updated."; return RedirectToAction(nameof(Playlist), new { id }); } [HttpPost] public async Task ReorderPlaylist([FromBody] ReorderRequest request) { if (request?.ItemIds == null) return BadRequest(); var deviceSlides = await _db.DeviceSlides .Where(ds => request.ItemIds.Contains(ds.Id)) .ToListAsync(); for (int i = 0; i < request.ItemIds.Length; i++) { var ds = deviceSlides.FirstOrDefault(x => x.Id == request.ItemIds[i]); if (ds != null) ds.DisplayOrder = i + 1; } await _db.SaveChangesAsync(); return Ok(); } [HttpPost] public async Task UpdateDuration([FromBody] UpdateDurationRequest request) { var ds = await _db.DeviceSlides.FindAsync(request.DeviceSlideId); if (ds == null) return NotFound(); ds.DurationSeconds = request.DurationSeconds; await _db.SaveChangesAsync(); return Ok(); } [HttpPost] public async Task ToggleEnabled([FromBody] ToggleEnabledRequest request) { var ds = await _db.DeviceSlides.FindAsync(request.DeviceSlideId); if (ds == null) return NotFound(); ds.Enabled = request.Enabled; await _db.SaveChangesAsync(); return Ok(); } } public class ReorderRequest { public int[]? ItemIds { get; set; } } public class UpdateDurationRequest { public int DeviceSlideId { get; set; } public int DurationSeconds { get; set; } } public class ToggleEnabledRequest { public int DeviceSlideId { get; set; } public bool Enabled { get; set; } }