From 1f3972ae49425cd2b2c754834744644dbd64c9be Mon Sep 17 00:00:00 2001 From: Adam Moussa <166072409+amoussa1229@users.noreply.github.com> Date: Mon, 22 Jun 2026 18:46:46 -0400 Subject: [PATCH] Add calendar/events backend API (#8) * Align EntityFrameworkCore.SqlServer and Tools to 8.0.8 * Add calendar/events backend API Cherry-picked from main-backup (19994ef); scratch notes file removed. * Require authentication on CalendarController Security review found [Authorize] commented out, leaving all 6 calendar endpoints anonymous. Enforce auth to match the API convention (17/23 controllers). * Add CalendarController unit tests (xUnit + EF InMemory) --- .../Api.SeaHavenIndustries.Tests.csproj | 25 ++ .../CalendarControllerTests.cs | 206 ++++++++++++ .../Controllers/CalendarController.cs | 306 ++++++++++++++++++ Api.SeaHavenIndustries/DTOs/EditEvent_DTO.cs | 48 +++ Api.SeaHavenIndustries/DTOs/Event_DTO.cs | 33 ++ .../Auth/ApplicationDbContext.cs | 1 + .../Data.SeaHavenIndustries.csproj | 2 +- Data.SeaHavenIndustries/Models/Events.cs | 49 +++ SeaHavenIndustries.sln | 6 + SeaHavenIndustries/SeaHavenIndustries.csproj | 4 +- 10 files changed, 677 insertions(+), 3 deletions(-) create mode 100644 Api.SeaHavenIndustries.Tests/Api.SeaHavenIndustries.Tests.csproj create mode 100644 Api.SeaHavenIndustries.Tests/CalendarControllerTests.cs create mode 100644 Api.SeaHavenIndustries/Controllers/CalendarController.cs create mode 100644 Api.SeaHavenIndustries/DTOs/EditEvent_DTO.cs create mode 100644 Api.SeaHavenIndustries/DTOs/Event_DTO.cs create mode 100644 Data.SeaHavenIndustries/Models/Events.cs diff --git a/Api.SeaHavenIndustries.Tests/Api.SeaHavenIndustries.Tests.csproj b/Api.SeaHavenIndustries.Tests/Api.SeaHavenIndustries.Tests.csproj new file mode 100644 index 0000000..01e908a --- /dev/null +++ b/Api.SeaHavenIndustries.Tests/Api.SeaHavenIndustries.Tests.csproj @@ -0,0 +1,25 @@ + + + + net8.0 + enable + enable + false + true + + + + + + + + + + + + + + + + + diff --git a/Api.SeaHavenIndustries.Tests/CalendarControllerTests.cs b/Api.SeaHavenIndustries.Tests/CalendarControllerTests.cs new file mode 100644 index 0000000..571b606 --- /dev/null +++ b/Api.SeaHavenIndustries.Tests/CalendarControllerTests.cs @@ -0,0 +1,206 @@ +using System.Collections; +using Api.SeaHavenIndustries.Controllers; +using Api.SeaHavenIndustries.DTOs; +using Data.SeaHavenIndustries; +using FluentAssertions; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Moq; +using Xunit; + +namespace Api.SeaHavenIndustries.Tests; + +public class CalendarControllerTests +{ + private static ApplicationDbContext NewContext() + { + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) + .Options; + return new ApplicationDbContext(options); + } + + private static UserManager MockUserManager() + { + var store = new Mock>(); + return new Mock>( + store.Object, null!, null!, null!, null!, null!, null!, null!, null!).Object; + } + + private static CalendarController NewController(ApplicationDbContext ctx) => + new(MockUserManager(), ctx); + + private static EditEvent_DTO ValidDto(int? id = null, string title = "Standup") => new() + { + Id = id, + Title = title, + ReminderMinutes = 15, + EventColor = "primary", + StartDate = new DateTime(2026, 6, 1), + StartTime = "08:00 AM", + EndDate = new DateTime(2026, 6, 1), + EndTime = "08:30 AM" + }; + + private static Events SeedEvent(ApplicationDbContext ctx, string title, DateTime startDate, bool? isDeleted = false) + { + var ev = new Events + { + Title = title, + EventColor = "primary", + ReminderMinutes = 15, + StartDate = startDate, + EndDate = startDate, + IsDeleted = isDeleted, + CreatedDate = DateTime.Now + }; + ctx.Events.Add(ev); + ctx.SaveChanges(); + return ev; + } + + private static object Prop(object source, string name) => + source.GetType().GetProperty(name)!.GetValue(source)!; + + [Fact] + public async Task AddEvent_WhenValid_PersistsEventAndReturnsOk() + { + using var ctx = NewContext(); + var result = await NewController(ctx).AddEvent(ValidDto()); + + var ok = result.Should().BeOfType().Subject; + ok.Value.Should().BeOfType().Which.Status.Should().Be("200"); + + var saved = ctx.Events.Single(); + saved.Title.Should().Be("Standup"); + saved.IsDeleted.Should().Be(false); + saved.CreatedDate.Should().NotBeNull(); + } + + [Fact] + public async Task EditEvent_WhenIdMissing_ReturnsBadRequest() + { + using var ctx = NewContext(); + var result = await NewController(ctx).EditEvent(ValidDto(id: null)); + + result.Should().BeOfType(); + } + + [Fact] + public async Task EditEvent_WhenEventNotFound_ReturnsNotFound() + { + using var ctx = NewContext(); + var result = await NewController(ctx).EditEvent(ValidDto(id: 999)); + + result.Should().BeOfType(); + } + + [Fact] + public async Task EditEvent_WhenEventExists_UpdatesFieldsAndStampsModified() + { + using var ctx = NewContext(); + var existing = SeedEvent(ctx, "Old", new DateTime(2026, 6, 1)); + + var result = await NewController(ctx).EditEvent(ValidDto(id: existing.Id, title: "New")); + + result.Should().BeOfType(); + var updated = ctx.Events.Single(); + updated.Title.Should().Be("New"); + updated.LastModificationTime.Should().NotBeNull(); + } + + [Fact] + public async Task GetEventById_WhenExists_ReturnsEventDto() + { + using var ctx = NewContext(); + var existing = SeedEvent(ctx, "Review", new DateTime(2026, 6, 1)); + + var result = await NewController(ctx).GetEventById(existing.Id); + + var ok = result.Should().BeOfType().Subject; + var dto = ok.Value.Should().BeOfType().Subject.Data as Event_DTO; + dto!.Title.Should().Be("Review"); + } + + [Fact] + public async Task GetEventById_WhenSoftDeleted_ReturnsNotFound() + { + using var ctx = NewContext(); + var deleted = SeedEvent(ctx, "Gone", new DateTime(2026, 6, 1), isDeleted: true); + + var result = await NewController(ctx).GetEventById(deleted.Id); + + result.Should().BeOfType(); + } + + [Fact] + public async Task DeleteEvent_WhenExists_SoftDeletesAndKeepsRow() + { + using var ctx = NewContext(); + var existing = SeedEvent(ctx, "Cancel", new DateTime(2026, 6, 1)); + + var result = await NewController(ctx).DeleteEvent(existing.Id); + + result.Should().BeOfType(); + var row = ctx.Events.Single(); + row.IsDeleted.Should().Be(true); + row.DeletionTime.Should().NotBeNull(); + } + + [Fact] + public async Task DeleteEvent_WhenNotFound_ReturnsNotFound() + { + using var ctx = NewContext(); + var result = await NewController(ctx).DeleteEvent(12345); + + result.Should().BeOfType(); + } + + [Fact] + public void GetEventList_ExcludesDeletedAndCountsAfterFilter() + { + using var ctx = NewContext(); + SeedEvent(ctx, "Alpha meeting", new DateTime(2026, 6, 1)); + SeedEvent(ctx, "Beta meeting", new DateTime(2026, 6, 2)); + SeedEvent(ctx, "Alpha review", new DateTime(2026, 6, 3)); + SeedEvent(ctx, "Alpha archived", new DateTime(2026, 6, 4), isDeleted: true); + + var result = NewController(ctx).GetEventList(search: "alpha", page: 1, pageSize: 10); + + var ok = result.Should().BeOfType().Subject; + var data = ok.Value.Should().BeOfType().Subject.Data!; + ((int)Prop(data, "totalCount")).Should().Be(2); + } + + [Fact] + public void GetEventList_OrdersByStartDateDescending() + { + using var ctx = NewContext(); + SeedEvent(ctx, "Earliest", new DateTime(2026, 6, 1)); + SeedEvent(ctx, "Latest", new DateTime(2026, 6, 10)); + SeedEvent(ctx, "Middle", new DateTime(2026, 6, 5)); + + var result = NewController(ctx).GetEventList(page: 1, pageSize: 10); + + var data = (result as OkObjectResult)!.Value.As().Data!; + var rows = ((IEnumerable)Prop(data, "rows")).Cast().ToList(); + rows.Select(r => r.Title).Should().ContainInOrder("Latest", "Middle", "Earliest"); + } + + [Fact] + public void GetEventsForCalendar_ReturnsOnlyEventsOverlappingRange() + { + using var ctx = NewContext(); + SeedEvent(ctx, "InRange", new DateTime(2026, 6, 15)); + SeedEvent(ctx, "BeforeRange", new DateTime(2026, 5, 1)); + SeedEvent(ctx, "AfterRange", new DateTime(2026, 7, 1)); + + var result = NewController(ctx).GetEventsForCalendar( + start: new DateTime(2026, 6, 1), end: new DateTime(2026, 6, 30)); + + var ok = result.Should().BeOfType().Subject; + var events = ok.Value.As().Data as IEnumerable; + events!.Select(e => e.Title).Should().BeEquivalentTo(new[] { "InRange" }); + } +} diff --git a/Api.SeaHavenIndustries/Controllers/CalendarController.cs b/Api.SeaHavenIndustries/Controllers/CalendarController.cs new file mode 100644 index 0000000..2d7f85d --- /dev/null +++ b/Api.SeaHavenIndustries/Controllers/CalendarController.cs @@ -0,0 +1,306 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.SeaHavenIndustries.DTOs; +using Data.SeaHavenIndustries; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace Api.SeaHavenIndustries.Controllers +{ + [Authorize] + [ApiController] + [Route("api/Calendar")] + public class CalendarController : Controller + { + private readonly UserManager _userManager; + private readonly ApplicationDbContext _db; + + public CalendarController(UserManager userManager, ApplicationDbContext db) + { + _userManager = userManager; + _db = db; + } + + // =================================================================================== + // CREATE EVENT + // =================================================================================== + [HttpPost] + [Route("AddEvent")] + public async Task AddEvent(EditEvent_DTO eventDto) + { + try + { + var model = new Events + { + Title = eventDto.Title, + Description = eventDto.Description, + Location = eventDto.Location, + RecordID = eventDto.RecordID, + RecordName = eventDto.RecordName, + RecordTargetType = eventDto.RecordTargetType, + ReminderMinutes = eventDto.ReminderMinutes, + EventColor = eventDto.EventColor, + StartDate = eventDto.StartDate, + StartTime = eventDto.StartTime, + EndDate = eventDto.EndDate, + EndTime = eventDto.EndTime, + AllDay = eventDto.AllDay, + CreatedDate = DateTime.Now, + IsDeleted = false + }; + + _db.Events.Add(model); + await _db.SaveChangesAsync(); + + return Ok(new DataResponse { Message = "Event Created Successfully", Status = "200" }); + } + catch (Exception ex) + { + return BadRequest(new Response { Status = "Error", Message = ex.Message }); + } + } + + // =================================================================================== + // UPDATE EVENT + // =================================================================================== + [HttpPost] + [Route("EditEvent")] + public async Task EditEvent(EditEvent_DTO eventDto) + { + try + { + if (!eventDto.Id.HasValue) + { + return BadRequest(new Response { Status = "Error", Message = "Event ID is required for editing" }); + } + + var model = await _db.Events.FindAsync(eventDto.Id.Value); + if (model == null || model.IsDeleted == true) + { + return NotFound(new Response { Status = "Error", Message = "Event not found" }); + } + + model.Title = eventDto.Title; + model.Description = eventDto.Description; + model.Location = eventDto.Location; + model.RecordID = eventDto.RecordID; + model.RecordName = eventDto.RecordName; + model.RecordTargetType = eventDto.RecordTargetType; + model.ReminderMinutes = eventDto.ReminderMinutes; + model.EventColor = eventDto.EventColor; + model.StartDate = eventDto.StartDate; + model.StartTime = eventDto.StartTime; + model.EndDate = eventDto.EndDate; + model.EndTime = eventDto.EndTime; + model.AllDay = eventDto.AllDay; + model.LastModificationTime = DateTime.Now; + + _db.Events.Update(model); + await _db.SaveChangesAsync(); + + return Ok(new DataResponse { Message = "Event Updated Successfully", Status = "200" }); + } + catch (Exception ex) + { + return BadRequest(new Response { Status = "Error", Message = ex.Message }); + } + } + + // =================================================================================== + // GET EVENT BY ID + // =================================================================================== + [HttpGet] + [Route("GetEventById/{id}")] + public async Task GetEventById(int id) + { + try + { + var model = await _db.Events + .Where(e => e.Id == id && e.IsDeleted != true) + .FirstOrDefaultAsync(); + + if (model == null) + { + return NotFound(new Response { Status = "Error", Message = "Event not found" }); + } + + var dto = new Event_DTO + { + Id = model.Id, + Title = model.Title, + Description = model.Description, + Location = model.Location, + RecordID = model.RecordID, + RecordName = model.RecordName, + RecordTargetType = model.RecordTargetType, + ReminderMinutes = model.ReminderMinutes, + EventColor = model.EventColor, + StartDate = model.StartDate, + StartTime = model.StartTime, + EndDate = model.EndDate, + EndTime = model.EndTime, + AllDay = model.AllDay, + CreatedDate = model.CreatedDate, + createdby = model.createdby + }; + + return Ok(new DataResponse { Message = "Success", Status = "200", Data = dto }); + } + catch (Exception ex) + { + return BadRequest(new Response { Status = "Error", Message = ex.Message }); + } + } + + // =================================================================================== + // DELETE EVENT + // =================================================================================== + [HttpDelete] + [Route("DeleteEvent/{id}")] + public async Task DeleteEvent(int id) + { + try + { + var model = await _db.Events.FindAsync(id); + if (model == null) + { + return NotFound(new Response { Status = "Error", Message = "Event not found" }); + } + + // Soft delete + model.IsDeleted = true; + model.DeletionTime = DateTime.Now; + + _db.Events.Update(model); + await _db.SaveChangesAsync(); + + return Ok(new DataResponse { Message = "Event Deleted Successfully", Status = "200" }); + } + catch (Exception ex) + { + return BadRequest(new Response { Status = "Error", Message = ex.Message }); + } + } + + // =================================================================================== + // GET EVENTS LIST (WITH PAGINATION AND FILTERS) + // =================================================================================== + [HttpGet] + [Route("GetEventList")] + public IActionResult GetEventList(string? search = "", int page = 1, int pageSize = 10) + { + try + { + var query = _db.Events.Where(e => e.IsDeleted != true); + + // Apply search filter if provided + if (!string.IsNullOrWhiteSpace(search)) + { + var searchLower = search.ToLower(); + query = query.Where(e => + e.Title.ToLower().Contains(searchLower) || + (e.Description != null && e.Description.ToLower().Contains(searchLower)) || + (e.Location != null && e.Location.ToLower().Contains(searchLower)) + ); + } + + // IMPORTANT: Calculate totalCount AFTER applying filters + var totalCount = query.Count(); + + // Apply pagination + var events = query + .OrderByDescending(e => e.StartDate) + .ThenByDescending(e => e.CreatedDate) + .Skip((page - 1) * pageSize) + .Take(pageSize) + .Select(e => new Event_DTO + { + Id = e.Id, + Title = e.Title, + Description = e.Description, + Location = e.Location, + RecordID = e.RecordID, + RecordName = e.RecordName, + RecordTargetType = e.RecordTargetType, + ReminderMinutes = e.ReminderMinutes, + EventColor = e.EventColor, + StartDate = e.StartDate, + StartTime = e.StartTime, + EndDate = e.EndDate, + EndTime = e.EndTime, + AllDay = e.AllDay, + CreatedDate = e.CreatedDate, + createdby = e.createdby + }) + .ToList(); + + return Ok(new DataResponse + { + Message = "Success", + Status = "200", + Data = new { rows = events, totalCount } + }); + } + catch (Exception ex) + { + return BadRequest(new Response { Status = "Error", Message = ex.Message }); + } + } + + // =================================================================================== + // GET EVENTS FOR CALENDAR (Date Range Filter) + // =================================================================================== + [HttpGet] + [Route("GetEventsForCalendar")] + public IActionResult GetEventsForCalendar([FromQuery] DateTime? start, [FromQuery] DateTime? end) + { + try + { + var query = _db.Events.Where(e => e.IsDeleted != true); + + // Filter by date range if provided + if (start.HasValue && end.HasValue) + { + query = query.Where(e => + (e.StartDate >= start.Value && e.StartDate <= end.Value) || + (e.EndDate >= start.Value && e.EndDate <= end.Value) || + (e.StartDate <= start.Value && e.EndDate >= end.Value) + ); + } + + var events = query + .Select(e => new Event_DTO + { + Id = e.Id, + Title = e.Title, + Description = e.Description, + Location = e.Location, + RecordID = e.RecordID, + RecordName = e.RecordName, + RecordTargetType = e.RecordTargetType, + ReminderMinutes = e.ReminderMinutes, + EventColor = e.EventColor, + StartDate = e.StartDate, + StartTime = e.StartTime, + EndDate = e.EndDate, + EndTime = e.EndTime, + AllDay = e.AllDay, + CreatedDate = e.CreatedDate, + createdby = e.createdby + }) + .ToList(); + + return Ok(new DataResponse { Message = "Success", Status = "200", Data = events }); + } + catch (Exception ex) + { + return BadRequest(new Response { Status = "Error", Message = ex.Message }); + } + } + } +} + diff --git a/Api.SeaHavenIndustries/DTOs/EditEvent_DTO.cs b/Api.SeaHavenIndustries/DTOs/EditEvent_DTO.cs new file mode 100644 index 0000000..0083de5 --- /dev/null +++ b/Api.SeaHavenIndustries/DTOs/EditEvent_DTO.cs @@ -0,0 +1,48 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Api.SeaHavenIndustries.DTOs +{ + /// + /// DTO for POST/PUT requests - Create/Edit Event + /// Used to create or update event information + /// + public class EditEvent_DTO + { + public int? Id { get; set; } // Null for create, populated for edit + + [Required(ErrorMessage = "Please enter a title")] + [StringLength(100, ErrorMessage = "Title cannot be longer than 100 characters")] + public string Title { get; set; } = null!; + + public string? Description { get; set; } + + [StringLength(150, ErrorMessage = "Location cannot be longer than 150 characters")] + public string? Location { get; set; } + + public int? RecordID { get; set; } + public string? RecordName { get; set; } + public string? RecordTargetType { get; set; } + + [Required(ErrorMessage = "The Remind Attendees field is required")] + public int ReminderMinutes { get; set; } = 15; + + [Required(ErrorMessage = "The Select a Calendar Color field is required")] + public string EventColor { get; set; } = "primary"; + + [Required(ErrorMessage = "Please select a start date")] + public DateTime StartDate { get; set; } + + [Required(ErrorMessage = "Please select a start time")] + public string StartTime { get; set; } = null!; + + [Required(ErrorMessage = "Please select an end date")] + public DateTime EndDate { get; set; } + + [Required(ErrorMessage = "Please select an end time")] + public string EndTime { get; set; } = null!; + + public bool AllDay { get; set; } = false; + } +} + diff --git a/Api.SeaHavenIndustries/DTOs/Event_DTO.cs b/Api.SeaHavenIndustries/DTOs/Event_DTO.cs new file mode 100644 index 0000000..2543831 --- /dev/null +++ b/Api.SeaHavenIndustries/DTOs/Event_DTO.cs @@ -0,0 +1,33 @@ +using System; + +namespace Api.SeaHavenIndustries.DTOs +{ + /// + /// DTO for GET responses - Event data + /// Used to return event information to the client + /// + public class Event_DTO + { + public int Id { get; set; } + public string Title { get; set; } = null!; + public string? Description { get; set; } + public string? Location { get; set; } + + public int? RecordID { get; set; } + public string? RecordName { get; set; } + public string? RecordTargetType { get; set; } + + public int ReminderMinutes { get; set; } + public string EventColor { get; set; } = null!; + + public DateTime StartDate { get; set; } + public string? StartTime { get; set; } + public DateTime EndDate { get; set; } + public string? EndTime { get; set; } + public bool AllDay { get; set; } + + public DateTime? CreatedDate { get; set; } + public string? createdby { get; set; } + } +} + diff --git a/Data.SeaHavenIndustries/Auth/ApplicationDbContext.cs b/Data.SeaHavenIndustries/Auth/ApplicationDbContext.cs index d66f708..ea60e8e 100644 --- a/Data.SeaHavenIndustries/Auth/ApplicationDbContext.cs +++ b/Data.SeaHavenIndustries/Auth/ApplicationDbContext.cs @@ -66,6 +66,7 @@ namespace Data.SeaHavenIndustries public DbSet FollowUps { get; set; } public DbSet PMSchedules { get; set; } public DbSet ContactDetails { get; set; } + public DbSet Events { get; set; } // Employee related DbSets public DbSet EmployeeAddresses { get; set; } diff --git a/Data.SeaHavenIndustries/Data.SeaHavenIndustries.csproj b/Data.SeaHavenIndustries/Data.SeaHavenIndustries.csproj index 183423b..4ee8133 100644 --- a/Data.SeaHavenIndustries/Data.SeaHavenIndustries.csproj +++ b/Data.SeaHavenIndustries/Data.SeaHavenIndustries.csproj @@ -9,7 +9,7 @@ - + diff --git a/Data.SeaHavenIndustries/Models/Events.cs b/Data.SeaHavenIndustries/Models/Events.cs new file mode 100644 index 0000000..97ed818 --- /dev/null +++ b/Data.SeaHavenIndustries/Models/Events.cs @@ -0,0 +1,49 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Data.SeaHavenIndustries +{ + /// + /// Events entity for Calendar/Events module + /// Tracks calendar events, appointments, meetings, etc. + /// + public class Events : FullAuditEntity + { + [Required] + [StringLength(100)] + public string Title { get; set; } = null!; + + public string? Description { get; set; } + + [StringLength(150)] + public string? Location { get; set; } + + // Related Record (can link to Accounts, Contacts, Work Orders, etc.) + public int? RecordID { get; set; } + public string? RecordName { get; set; } + public string? RecordTargetType { get; set; } + + // Reminder Settings + public int ReminderMinutes { get; set; } = 15; // Default: 15 minutes before + + // Color for calendar display + [StringLength(20)] + public string EventColor { get; set; } = "primary"; // primary, success, info, warning, danger + + // Schedule + [Required] + public DateTime StartDate { get; set; } + + [StringLength(10)] + public string? StartTime { get; set; } // e.g., "08:00 AM" + + [Required] + public DateTime EndDate { get; set; } + + [StringLength(10)] + public string? EndTime { get; set; } // e.g., "08:30 AM" + + public bool AllDay { get; set; } = false; + } +} + diff --git a/SeaHavenIndustries.sln b/SeaHavenIndustries.sln index 5560d76..b9df8e5 100644 --- a/SeaHavenIndustries.sln +++ b/SeaHavenIndustries.sln @@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SeaHaven.Services", "SeaHav EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SeaHaven.DataServices", "SeaHaven.DataServices\SeaHaven.DataServices.csproj", "{60DF3943-9BC3-4444-8E7A-EEC34DDC7806}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.SeaHavenIndustries.Tests", "Api.SeaHavenIndustries.Tests\Api.SeaHavenIndustries.Tests.csproj", "{1222E2E5-2FB0-4AA8-842C-0D77CFBA5E45}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {60DF3943-9BC3-4444-8E7A-EEC34DDC7806}.Debug|Any CPU.Build.0 = Debug|Any CPU {60DF3943-9BC3-4444-8E7A-EEC34DDC7806}.Release|Any CPU.ActiveCfg = Release|Any CPU {60DF3943-9BC3-4444-8E7A-EEC34DDC7806}.Release|Any CPU.Build.0 = Release|Any CPU + {1222E2E5-2FB0-4AA8-842C-0D77CFBA5E45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1222E2E5-2FB0-4AA8-842C-0D77CFBA5E45}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1222E2E5-2FB0-4AA8-842C-0D77CFBA5E45}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1222E2E5-2FB0-4AA8-842C-0D77CFBA5E45}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SeaHavenIndustries/SeaHavenIndustries.csproj b/SeaHavenIndustries/SeaHavenIndustries.csproj index 11de19e..f3b6061 100644 --- a/SeaHavenIndustries/SeaHavenIndustries.csproj +++ b/SeaHavenIndustries/SeaHavenIndustries.csproj @@ -13,8 +13,8 @@ - - + +