mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-07 16:19:07 +00:00
refactor(api): enforce service and data-service boundaries
This commit is contained in:
parent
4b4ab04615
commit
aa59399b78
38 changed files with 1924 additions and 506 deletions
|
|
@ -3,34 +3,16 @@ 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 SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using Xunit;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Tests;
|
||||
|
||||
public class CalendarControllerTests
|
||||
{
|
||||
private static ApplicationDbContext NewContext()
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
||||
.Options;
|
||||
return new ApplicationDbContext(options);
|
||||
}
|
||||
|
||||
private static UserManager<ApplicationUser> MockUserManager()
|
||||
{
|
||||
var store = new Mock<IUserStore<ApplicationUser>>();
|
||||
return new Mock<UserManager<ApplicationUser>>(
|
||||
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,
|
||||
|
|
@ -43,164 +25,166 @@ public class CalendarControllerTests
|
|||
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 CalendarController NewController(Mock<ICalendarService> service) =>
|
||||
new(service.Object);
|
||||
|
||||
private static object Prop(object source, string name) =>
|
||||
source.GetType().GetProperty(name)!.GetValue(source)!;
|
||||
|
||||
[Fact]
|
||||
public async Task AddEvent_WhenValid_PersistsEventAndReturnsOk()
|
||||
public async Task AddEvent_WhenServiceSucceeds_ReturnsOkWithCreatedMessage()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var result = await NewController(ctx).AddEvent(ValidDto());
|
||||
var service = new Mock<ICalendarService>();
|
||||
var result = await NewController(service).AddEvent(ValidDto(), CancellationToken.None);
|
||||
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
ok.Value.Should().BeOfType<DataResponse>().Which.Status.Should().Be("200");
|
||||
|
||||
var saved = ctx.Events.Single();
|
||||
saved.Title.Should().Be("Standup");
|
||||
saved.IsDeleted.Should().Be(false);
|
||||
saved.CreatedDate.Should().NotBeNull();
|
||||
var response = ok.Value.Should().BeOfType<DataResponse>().Subject;
|
||||
response.Status.Should().Be("200");
|
||||
response.Message.Should().Be("Event Created Successfully");
|
||||
service.Verify(s => s.CreateEventAsync(It.IsAny<EditEventInput>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EditEvent_WhenIdMissing_ReturnsBadRequest()
|
||||
public async Task AddEvent_WhenServiceThrows_ReturnsBadRequestWithMessage()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var result = await NewController(ctx).EditEvent(ValidDto(id: null));
|
||||
var service = new Mock<ICalendarService>();
|
||||
service.Setup(s => s.CreateEventAsync(It.IsAny<EditEventInput>(), It.IsAny<CancellationToken>()))
|
||||
.ThrowsAsync(new InvalidOperationException("boom"));
|
||||
|
||||
result.Should().BeOfType<BadRequestObjectResult>();
|
||||
var result = await NewController(service).AddEvent(ValidDto(), CancellationToken.None);
|
||||
|
||||
var bad = result.Should().BeOfType<BadRequestObjectResult>().Subject;
|
||||
var response = bad.Value.Should().BeOfType<Response>().Subject;
|
||||
response.Message.Should().Be("boom");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EditEvent_WhenEventNotFound_ReturnsNotFound()
|
||||
public async Task EditEvent_WhenIdMissing_ReturnsBadRequestWithoutCallingService()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var result = await NewController(ctx).EditEvent(ValidDto(id: 999));
|
||||
var service = new Mock<ICalendarService>();
|
||||
var result = await NewController(service).EditEvent(ValidDto(id: null), CancellationToken.None);
|
||||
|
||||
var bad = result.Should().BeOfType<BadRequestObjectResult>().Subject;
|
||||
var response = bad.Value.Should().BeOfType<Response>().Subject;
|
||||
response.Message.Should().Be("Event ID is required for editing");
|
||||
service.Verify(s => s.UpdateEventAsync(It.IsAny<EditEventInput>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EditEvent_WhenServiceReturnsFalse_ReturnsNotFound()
|
||||
{
|
||||
var service = new Mock<ICalendarService>();
|
||||
service.Setup(s => s.UpdateEventAsync(It.IsAny<EditEventInput>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(false);
|
||||
|
||||
var result = await NewController(service).EditEvent(ValidDto(id: 5), CancellationToken.None);
|
||||
|
||||
var nf = result.Should().BeOfType<NotFoundObjectResult>().Subject;
|
||||
var response = nf.Value.Should().BeOfType<Response>().Subject;
|
||||
response.Message.Should().Be("Event not found");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EditEvent_WhenServiceReturnsTrue_ReturnsOkWithUpdatedMessage()
|
||||
{
|
||||
var service = new Mock<ICalendarService>();
|
||||
service.Setup(s => s.UpdateEventAsync(It.IsAny<EditEventInput>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(true);
|
||||
|
||||
var result = await NewController(service).EditEvent(ValidDto(id: 5, title: "New"), CancellationToken.None);
|
||||
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
ok.Value.Should().BeOfType<DataResponse>().Which.Message.Should().Be("Event Updated Successfully");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetEventById_WhenServiceReturnsNull_ReturnsNotFound()
|
||||
{
|
||||
var service = new Mock<ICalendarService>();
|
||||
service.Setup(s => s.GetEventByIdAsync(7, It.IsAny<CancellationToken>())).ReturnsAsync((EventDTO?)null);
|
||||
|
||||
var result = await NewController(service).GetEventById(7, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<NotFoundObjectResult>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EditEvent_WhenEventExists_UpdatesFieldsAndStampsModified()
|
||||
public async Task GetEventById_WhenServiceReturnsDto_ReturnsOkWithEventDto()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var existing = SeedEvent(ctx, "Old", new DateTime(2026, 6, 1));
|
||||
var service = new Mock<ICalendarService>();
|
||||
service.Setup(s => s.GetEventByIdAsync(7, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new EventDTO { Id = 7, Title = "Review", createdby = "abc" });
|
||||
|
||||
var result = await NewController(ctx).EditEvent(ValidDto(id: existing.Id, title: "New"));
|
||||
|
||||
result.Should().BeOfType<OkObjectResult>();
|
||||
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 result = await NewController(service).GetEventById(7, CancellationToken.None);
|
||||
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
var dto = ok.Value.Should().BeOfType<DataResponse>().Subject.Data as Event_DTO;
|
||||
var response = ok.Value.Should().BeOfType<DataResponse>().Subject;
|
||||
var dto = response.Data as Event_DTO;
|
||||
dto.Should().NotBeNull();
|
||||
dto!.Title.Should().Be("Review");
|
||||
dto!.createdby.Should().Be("abc");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetEventById_WhenSoftDeleted_ReturnsNotFound()
|
||||
public async Task DeleteEvent_WhenServiceReturnsFalse_ReturnsNotFound()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var deleted = SeedEvent(ctx, "Gone", new DateTime(2026, 6, 1), isDeleted: true);
|
||||
var service = new Mock<ICalendarService>();
|
||||
service.Setup(s => s.DeleteEventAsync(9, It.IsAny<CancellationToken>())).ReturnsAsync(false);
|
||||
|
||||
var result = await NewController(ctx).GetEventById(deleted.Id);
|
||||
var result = await NewController(service).DeleteEvent(9, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<NotFoundObjectResult>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteEvent_WhenExists_SoftDeletesAndKeepsRow()
|
||||
public async Task DeleteEvent_WhenServiceReturnsTrue_ReturnsOkWithDeletedMessage()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var existing = SeedEvent(ctx, "Cancel", new DateTime(2026, 6, 1));
|
||||
var service = new Mock<ICalendarService>();
|
||||
service.Setup(s => s.DeleteEventAsync(9, It.IsAny<CancellationToken>())).ReturnsAsync(true);
|
||||
|
||||
var result = await NewController(ctx).DeleteEvent(existing.Id);
|
||||
var result = await NewController(service).DeleteEvent(9, CancellationToken.None);
|
||||
|
||||
result.Should().BeOfType<OkObjectResult>();
|
||||
var row = ctx.Events.Single();
|
||||
row.IsDeleted.Should().Be(true);
|
||||
row.DeletionTime.Should().NotBeNull();
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
ok.Value.Should().BeOfType<DataResponse>().Which.Message.Should().Be("Event Deleted Successfully");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteEvent_WhenNotFound_ReturnsNotFound()
|
||||
public async Task GetEventList_ReturnsOkWithRowsAndTotalCount()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var result = await NewController(ctx).DeleteEvent(12345);
|
||||
var service = new Mock<ICalendarService>();
|
||||
service.Setup(s => s.GetEventListAsync("alpha", 1, 10, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new PagedEventsResult
|
||||
{
|
||||
TotalCount = 2,
|
||||
Rows = new List<EventDTO>
|
||||
{
|
||||
new() { Id = 1, Title = "Alpha meeting" },
|
||||
new() { Id = 2, Title = "Alpha review" }
|
||||
}
|
||||
});
|
||||
|
||||
result.Should().BeOfType<NotFoundObjectResult>();
|
||||
}
|
||||
|
||||
[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 result = await NewController(service).GetEventList(search: "alpha", page: 1, pageSize: 10);
|
||||
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
var data = ok.Value.Should().BeOfType<DataResponse>().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<DataResponse>().Data!;
|
||||
var rows = ((IEnumerable)Prop(data, "rows")).Cast<Event_DTO>().ToList();
|
||||
rows.Select(r => r.Title).Should().ContainInOrder("Latest", "Middle", "Earliest");
|
||||
rows.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetEventsForCalendar_ReturnsOnlyEventsOverlappingRange()
|
||||
public async Task GetEventsForCalendar_ReturnsOkWithEvents()
|
||||
{
|
||||
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 service = new Mock<ICalendarService>();
|
||||
service.Setup(s => s.GetEventsForCalendarAsync(It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new List<EventDTO> { new() { Id = 1, Title = "InRange" } });
|
||||
|
||||
var result = NewController(ctx).GetEventsForCalendar(
|
||||
start: new DateTime(2026, 6, 1), end: new DateTime(2026, 6, 30));
|
||||
var result = await NewController(service).GetEventsForCalendar(
|
||||
start: new DateTime(2026, 6, 1), end: new DateTime(2026, 6, 30), CancellationToken.None);
|
||||
|
||||
var ok = result.Should().BeOfType<OkObjectResult>().Subject;
|
||||
var events = ok.Value.As<DataResponse>().Data as IEnumerable<Event_DTO>;
|
||||
events!.Select(e => e.Title).Should().BeEquivalentTo(new[] { "InRange" });
|
||||
var data = ok.Value.Should().BeOfType<DataResponse>().Subject.Data;
|
||||
var events = ((IEnumerable)data!).Cast<Event_DTO>().ToList();
|
||||
events.Single().Title.Should().Be("InRange");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
188
Api.SeaHavenIndustries.Tests/CalendarServiceTests.cs
Normal file
188
Api.SeaHavenIndustries.Tests/CalendarServiceTests.cs
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Data.SeaHavenIndustries;
|
||||
using FluentAssertions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Implementation;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Implementation;
|
||||
using Xunit;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Tests;
|
||||
|
||||
public class CalendarServiceTests
|
||||
{
|
||||
private static ApplicationDbContext NewContext()
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
||||
.Options;
|
||||
return new ApplicationDbContext(options);
|
||||
}
|
||||
|
||||
private static CalendarService NewService(ApplicationDbContext ctx) =>
|
||||
new(new CalendarDataService(ctx));
|
||||
|
||||
private static EditEventInput ValidInput(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 CreateEventAsync_PersistsEventWithMappedFieldsAndCreatedDate()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
await NewService(ctx).CreateEventAsync(ValidInput(), CancellationToken.None);
|
||||
|
||||
var saved = ctx.Events.Single();
|
||||
saved.Title.Should().Be("Standup");
|
||||
saved.IsDeleted.Should().Be(false);
|
||||
saved.CreatedDate.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateEventAsync_WhenNotFound_ReturnsFalse()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var result = await NewService(ctx).UpdateEventAsync(ValidInput(id: 999), CancellationToken.None);
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateEventAsync_WhenSoftDeleted_ReturnsFalse()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var existing = SeedEvent(ctx, "Old", new DateTime(2026, 6, 1), isDeleted: true);
|
||||
|
||||
var result = await NewService(ctx).UpdateEventAsync(ValidInput(id: existing.Id, title: "New"), CancellationToken.None);
|
||||
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateEventAsync_WhenEventExists_UpdatesFieldsAndStampsModified()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var existing = SeedEvent(ctx, "Old", new DateTime(2026, 6, 1));
|
||||
|
||||
var result = await NewService(ctx).UpdateEventAsync(ValidInput(id: existing.Id, title: "New"), CancellationToken.None);
|
||||
|
||||
result.Should().BeTrue();
|
||||
var updated = ctx.Events.Single();
|
||||
updated.Title.Should().Be("New");
|
||||
updated.LastModificationTime.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetEventByIdAsync_WhenExists_ReturnsMappedDto()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var existing = SeedEvent(ctx, "Review", new DateTime(2026, 6, 1));
|
||||
|
||||
var dto = await NewService(ctx).GetEventByIdAsync(existing.Id, CancellationToken.None);
|
||||
|
||||
dto.Should().NotBeNull();
|
||||
dto!.Title.Should().Be("Review");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetEventByIdAsync_WhenSoftDeleted_ReturnsNull()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var deleted = SeedEvent(ctx, "Gone", new DateTime(2026, 6, 1), isDeleted: true);
|
||||
|
||||
var dto = await NewService(ctx).GetEventByIdAsync(deleted.Id, CancellationToken.None);
|
||||
|
||||
dto.Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteEventAsync_WhenExists_SoftDeletesAndKeepsRow()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var existing = SeedEvent(ctx, "Cancel", new DateTime(2026, 6, 1));
|
||||
|
||||
var result = await NewService(ctx).DeleteEventAsync(existing.Id, CancellationToken.None);
|
||||
|
||||
result.Should().BeTrue();
|
||||
var row = ctx.Events.Single();
|
||||
row.IsDeleted.Should().Be(true);
|
||||
row.DeletionTime.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteEventAsync_WhenNotFound_ReturnsFalse()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var result = await NewService(ctx).DeleteEventAsync(12345, CancellationToken.None);
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetEventListAsync_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 = await NewService(ctx).GetEventListAsync("alpha", 1, 10, CancellationToken.None);
|
||||
|
||||
result.TotalCount.Should().Be(2);
|
||||
result.Rows.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetEventListAsync_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 = await NewService(ctx).GetEventListAsync(null, 1, 10, CancellationToken.None);
|
||||
|
||||
result.Rows.Select(r => r.Title).Should().ContainInOrder("Latest", "Middle", "Earliest");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetEventsForCalendarAsync_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 events = await NewService(ctx).GetEventsForCalendarAsync(
|
||||
new DateTime(2026, 6, 1), new DateTime(2026, 6, 30), CancellationToken.None);
|
||||
|
||||
events.Select(e => e.Title).Should().BeEquivalentTo(new[] { "InRange" });
|
||||
}
|
||||
}
|
||||
49
Api.SeaHavenIndustries.Tests/DashboardServiceTests.cs
Normal file
49
Api.SeaHavenIndustries.Tests/DashboardServiceTests.cs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using FluentAssertions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Implementation;
|
||||
using SeaHaven.Services.Implementation;
|
||||
using Xunit;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Tests;
|
||||
|
||||
public class DashboardServiceTests
|
||||
{
|
||||
private static ApplicationDbContext NewContext()
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
||||
.Options;
|
||||
return new ApplicationDbContext(options);
|
||||
}
|
||||
|
||||
private static DashboardService NewService(ApplicationDbContext ctx) =>
|
||||
new(new DashboardDataService(ctx));
|
||||
|
||||
private static WorkOrder Wo(string? status, bool? isTemplate = false, string? assignTo = null) =>
|
||||
new() { Status = status, istemplate = isTemplate, AssignTo = assignTo };
|
||||
|
||||
[Fact]
|
||||
public async Task GetStatsAsync_CountsByStatusExcludingTemplates()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
ctx.workOrders.AddRange(
|
||||
Wo("Open", isTemplate: false),
|
||||
Wo("Open", isTemplate: false, assignTo: "u1"),
|
||||
Wo("In Progress"),
|
||||
Wo("On Hold"),
|
||||
Wo("Done"),
|
||||
Wo("Done"),
|
||||
Wo("Open", isTemplate: true),
|
||||
Wo("Cancelled", isTemplate: false)
|
||||
);
|
||||
ctx.SaveChanges();
|
||||
|
||||
var stats = await NewService(ctx).GetStatsAsync(CancellationToken.None);
|
||||
|
||||
stats.Total.Should().Be(7);
|
||||
stats.Open.Should().Be(4);
|
||||
stats.NotDispatched.Should().Be(1);
|
||||
stats.Completed.Should().Be(2);
|
||||
}
|
||||
}
|
||||
146
Api.SeaHavenIndustries.Tests/DropdownOptionsServiceTests.cs
Normal file
146
Api.SeaHavenIndustries.Tests/DropdownOptionsServiceTests.cs
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using FluentAssertions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Implementation;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Implementation;
|
||||
using Xunit;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Tests;
|
||||
|
||||
public class DropdownOptionsServiceTests
|
||||
{
|
||||
private static ApplicationDbContext NewContext()
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
||||
.Options;
|
||||
return new ApplicationDbContext(options);
|
||||
}
|
||||
|
||||
private static DropdownOptionsService NewService(ApplicationDbContext ctx) =>
|
||||
new(new DropdownOptionsDataService(ctx));
|
||||
|
||||
private static DropdownOption Option(string category, string value, string? parent = null, int sort = 0, bool active = true) =>
|
||||
new() { Category = category, Value = value, ParentValue = parent, SortOrder = sort, IsActive = active };
|
||||
|
||||
[Fact]
|
||||
public async Task GetAllAsync_FiltersActiveAndOrdersBySortThenValue()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
ctx.DropdownOptions.AddRange(
|
||||
Option("Color", "Red", sort: 2),
|
||||
Option("Color", "Blue", sort: 1),
|
||||
Option("Color", "Green", sort: 1, active: false),
|
||||
Option("Shape", "Circle")
|
||||
);
|
||||
ctx.SaveChanges();
|
||||
|
||||
var result = (await NewService(ctx).GetAllAsync(null, CancellationToken.None)).ToList();
|
||||
|
||||
result.Should().HaveCount(3);
|
||||
result.Select(d => d.Value).Should().ContainInOrder("Blue", "Red", "Circle");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetByCategoryAsync_FiltersByCategoryAndParent()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
ctx.DropdownOptions.AddRange(
|
||||
Option("State", "TX", parent: "US"),
|
||||
Option("State", "CA", parent: "US"),
|
||||
Option("State", "ON", parent: "CA")
|
||||
);
|
||||
ctx.SaveChanges();
|
||||
|
||||
var result = (await NewService(ctx).GetByCategoryAsync("State", "US", CancellationToken.None)).ToList();
|
||||
|
||||
result.Select(d => d.Value).Should().BeEquivalentTo(new[] { "TX", "CA" });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateAsync_WhenCategoryOrValueMissing_Throws()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var act = () => NewService(ctx).CreateAsync(new DropdownOptionDTO { Category = "", Value = "X" }, CancellationToken.None);
|
||||
await act.Should().ThrowAsync<InvalidOperationException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateAsync_WhenDuplicate_Throws()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
ctx.DropdownOptions.Add(Option("Color", "Red", parent: "Primary"));
|
||||
ctx.SaveChanges();
|
||||
|
||||
var act = () => NewService(ctx).CreateAsync(new DropdownOptionDTO { Category = "Color", Value = "Red", ParentValue = "Primary" }, CancellationToken.None);
|
||||
|
||||
await act.Should().ThrowAsync<InvalidOperationException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateAsync_ForcesActiveTrueAndReturnsDtoWithId()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var dto = await NewService(ctx).CreateAsync(
|
||||
new DropdownOptionDTO { Category = "Color", Value = "Red", IsActive = false }, CancellationToken.None);
|
||||
|
||||
dto.Id.Should().BeGreaterThan(0);
|
||||
dto.IsActive.Should().BeTrue();
|
||||
ctx.DropdownOptions.Single().IsActive.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateAsync_WhenNotFound_ThrowsKeyNotFound()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var act = () => NewService(ctx).UpdateAsync(404, new DropdownOptionDTO { Category = "Color", Value = "Red" }, CancellationToken.None);
|
||||
await act.Should().ThrowAsync<KeyNotFoundException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateAsync_UpdatesValueParentSortActiveButNotCategory()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var existing = Option("Color", "Red");
|
||||
ctx.DropdownOptions.Add(existing);
|
||||
ctx.SaveChanges();
|
||||
|
||||
await NewService(ctx).UpdateAsync(existing.Id, new DropdownOptionDTO
|
||||
{
|
||||
Category = "Ignored",
|
||||
Value = "Crimson",
|
||||
ParentValue = "Warm",
|
||||
SortOrder = 5,
|
||||
IsActive = false
|
||||
}, CancellationToken.None);
|
||||
|
||||
var row = ctx.DropdownOptions.Single();
|
||||
row.Category.Should().Be("Color");
|
||||
row.Value.Should().Be("Crimson");
|
||||
row.ParentValue.Should().Be("Warm");
|
||||
row.SortOrder.Should().Be(5);
|
||||
row.IsActive.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteAsync_WhenNotFound_ThrowsKeyNotFound()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var act = () => NewService(ctx).DeleteAsync(404, CancellationToken.None);
|
||||
await act.Should().ThrowAsync<KeyNotFoundException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteAsync_RemovesOption()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var existing = Option("Color", "Red");
|
||||
ctx.DropdownOptions.Add(existing);
|
||||
ctx.SaveChanges();
|
||||
|
||||
await NewService(ctx).DeleteAsync(existing.Id, CancellationToken.None);
|
||||
|
||||
ctx.DropdownOptions.Should().BeEmpty();
|
||||
}
|
||||
}
|
||||
57
Api.SeaHavenIndustries.Tests/ServiceRegistrationTests.cs
Normal file
57
Api.SeaHavenIndustries.Tests/ServiceRegistrationTests.cs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SeaHaven.DataServices.DependencyInjection;
|
||||
using SeaHaven.DataServices.Implementation;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DependencyInjection;
|
||||
using SeaHaven.Services.Implementation;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
using Xunit;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Tests;
|
||||
|
||||
public class ServiceRegistrationTests
|
||||
{
|
||||
private static ServiceProvider BuildProvider()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging();
|
||||
|
||||
services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()));
|
||||
|
||||
services.AddIdentity<ApplicationUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>();
|
||||
|
||||
services.AddDataServices();
|
||||
services.AddBusinessServices();
|
||||
|
||||
return services.BuildServiceProvider();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConventionDi_RegistersAllFiveFeatureServices()
|
||||
{
|
||||
using var provider = BuildProvider();
|
||||
|
||||
provider.GetRequiredService<ICalendarService>().Should().BeOfType<CalendarService>();
|
||||
provider.GetRequiredService<ICommonService>().Should().BeOfType<CommonService>();
|
||||
provider.GetRequiredService<IDashboardService>().Should().BeOfType<DashboardService>();
|
||||
provider.GetRequiredService<IDropdownOptionsService>().Should().BeOfType<DropdownOptionsService>();
|
||||
provider.GetRequiredService<ITaskListTemplateService>().Should().BeOfType<TaskListTemplateService>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConventionDi_RegistersFeatureDataServices()
|
||||
{
|
||||
using var provider = BuildProvider();
|
||||
|
||||
provider.GetRequiredService<ICalendarDataService>().Should().BeOfType<CalendarDataService>();
|
||||
provider.GetRequiredService<ICommonDataService>().Should().BeOfType<CommonDataService>();
|
||||
provider.GetRequiredService<IDashboardDataService>().Should().BeOfType<DashboardDataService>();
|
||||
provider.GetRequiredService<IDropdownOptionsDataService>().Should().BeOfType<DropdownOptionsDataService>();
|
||||
}
|
||||
}
|
||||
146
Api.SeaHavenIndustries.Tests/TaskListTemplateServiceTests.cs
Normal file
146
Api.SeaHavenIndustries.Tests/TaskListTemplateServiceTests.cs
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using FluentAssertions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Implementation;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Implementation;
|
||||
using Xunit;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Tests;
|
||||
|
||||
public class TaskListTemplateServiceTests
|
||||
{
|
||||
private static ApplicationDbContext NewContext()
|
||||
{
|
||||
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
|
||||
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
||||
.Options;
|
||||
return new ApplicationDbContext(options);
|
||||
}
|
||||
|
||||
private static TaskListTemplateService NewService(ApplicationDbContext ctx) =>
|
||||
new(new TaskListTemplateDataService(ctx));
|
||||
|
||||
private static TaskListTemplate Template(string name, bool active, params (string text, int sort)[] items)
|
||||
{
|
||||
var t = new TaskListTemplate { Name = name, IsActive = active };
|
||||
t.Items = items.Select(i => new TaskListTemplateItem { ItemText = i.text, SortOrder = i.sort }).ToList();
|
||||
return t;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetAllAsync_ReturnsOnlyActiveOrderedByNameWithOrderedItems()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
ctx.TaskListTemplates.AddRange(
|
||||
Template("Zeta", true, ("b", 2), ("a", 1)),
|
||||
Template("Alpha", true, ("first", 0)),
|
||||
Template("Hidden", false)
|
||||
);
|
||||
ctx.SaveChanges();
|
||||
|
||||
var result = (await NewService(ctx).GetAllAsync(CancellationToken.None)).ToList();
|
||||
|
||||
result.Select(t => t.Name).Should().ContainInOrder("Alpha", "Zeta");
|
||||
result.Single(t => t.Name == "Zeta").Items.Select(i => i.ItemText).Should().ContainInOrder("a", "b");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetByIdAsync_WhenMissing_ReturnsNull()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var result = await NewService(ctx).GetByIdAsync(99, CancellationToken.None);
|
||||
result.Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetByIdAsync_ReturnsTemplateWithOrderedItems()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var t = Template("Alpha", true, ("b", 2), ("a", 1));
|
||||
ctx.TaskListTemplates.Add(t);
|
||||
ctx.SaveChanges();
|
||||
|
||||
var result = await NewService(ctx).GetByIdAsync(t.Id, CancellationToken.None);
|
||||
|
||||
result.Should().NotBeNull();
|
||||
result!.Items.Select(i => i.ItemText).Should().ContainInOrder("a", "b");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateAsync_PersistsTemplateAndItemsAndReturnsId()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var input = new TaskListTemplateInput
|
||||
{
|
||||
Name = "New",
|
||||
Description = "desc",
|
||||
IsActive = true,
|
||||
Items = new()
|
||||
{
|
||||
new() { ItemText = "two", SortOrder = 0 },
|
||||
new() { ItemText = "one", SortOrder = 0 }
|
||||
}
|
||||
};
|
||||
|
||||
var id = await NewService(ctx).CreateAsync(input, CancellationToken.None);
|
||||
|
||||
id.Should().BeGreaterThan(0);
|
||||
var row = ctx.TaskListTemplates.Include(t => t.Items).Single();
|
||||
row.Name.Should().Be("New");
|
||||
row.Items.Should().HaveCount(2);
|
||||
row.Items!.Select(i => i.SortOrder).Should().ContainInOrder(0, 1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateAsync_WhenNotFound_ReturnsFalse()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var result = await NewService(ctx).UpdateAsync(404, new TaskListTemplateInput { Name = "x" }, CancellationToken.None);
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateAsync_ReplacesItems()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var t = Template("Alpha", true, ("old", 0));
|
||||
ctx.TaskListTemplates.Add(t);
|
||||
ctx.SaveChanges();
|
||||
|
||||
var updated = await NewService(ctx).UpdateAsync(t.Id, new TaskListTemplateInput
|
||||
{
|
||||
Name = "Renamed",
|
||||
IsActive = false,
|
||||
Items = new() { new() { ItemText = "new1", SortOrder = 1 }, new() { ItemText = "new2", SortOrder = 2 } }
|
||||
}, CancellationToken.None);
|
||||
|
||||
updated.Should().BeTrue();
|
||||
var row = ctx.TaskListTemplates.Include(x => x.Items).Single();
|
||||
row.Name.Should().Be("Renamed");
|
||||
row.IsActive.Should().BeFalse();
|
||||
row.Items!.Select(i => i.ItemText).Should().BeEquivalentTo(new[] { "new1", "new2" });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteAsync_SoftDeletesByDeactivating()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var t = Template("Alpha", true);
|
||||
ctx.TaskListTemplates.Add(t);
|
||||
ctx.SaveChanges();
|
||||
|
||||
var result = await NewService(ctx).DeleteAsync(t.Id, CancellationToken.None);
|
||||
|
||||
result.Should().BeTrue();
|
||||
ctx.TaskListTemplates.Single().IsActive.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteAsync_WhenNotFound_ReturnsFalse()
|
||||
{
|
||||
using var ctx = NewContext();
|
||||
var result = await NewService(ctx).DeleteAsync(404, CancellationToken.None);
|
||||
result.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,9 @@
|
|||
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;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
|
|
@ -16,13 +12,11 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
[Route("api/Calendar")]
|
||||
public class CalendarController : Controller
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly ApplicationDbContext _db;
|
||||
private readonly ICalendarService _calendarService;
|
||||
|
||||
public CalendarController(UserManager<ApplicationUser> userManager, ApplicationDbContext db)
|
||||
public CalendarController(ICalendarService calendarService)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_db = db;
|
||||
_calendarService = calendarService;
|
||||
}
|
||||
|
||||
// ===================================================================================
|
||||
|
|
@ -30,31 +24,11 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
// ===================================================================================
|
||||
[HttpPost]
|
||||
[Route("AddEvent")]
|
||||
public async Task<IActionResult> AddEvent(EditEvent_DTO eventDto)
|
||||
public async Task<IActionResult> AddEvent(EditEvent_DTO eventDto, CancellationToken cancellationToken)
|
||||
{
|
||||
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();
|
||||
await _calendarService.CreateEventAsync(MapToInput(eventDto), cancellationToken);
|
||||
|
||||
return Ok(new DataResponse { Message = "Event Created Successfully", Status = "200" });
|
||||
}
|
||||
|
|
@ -69,7 +43,7 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
// ===================================================================================
|
||||
[HttpPost]
|
||||
[Route("EditEvent")]
|
||||
public async Task<IActionResult> EditEvent(EditEvent_DTO eventDto)
|
||||
public async Task<IActionResult> EditEvent(EditEvent_DTO eventDto, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -78,30 +52,12 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
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)
|
||||
var updated = await _calendarService.UpdateEventAsync(MapToInput(eventDto), cancellationToken);
|
||||
if (!updated)
|
||||
{
|
||||
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)
|
||||
|
|
@ -115,40 +71,18 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
// ===================================================================================
|
||||
[HttpGet]
|
||||
[Route("GetEventById/{id}")]
|
||||
public async Task<IActionResult> GetEventById(int id)
|
||||
public async Task<IActionResult> GetEventById(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var model = await _db.Events
|
||||
.Where(e => e.Id == id && e.IsDeleted != true)
|
||||
.FirstOrDefaultAsync();
|
||||
var dto = await _calendarService.GetEventByIdAsync(id, cancellationToken);
|
||||
|
||||
if (model == null)
|
||||
if (dto == 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 });
|
||||
return Ok(new DataResponse { Message = "Success", Status = "200", Data = MapToResponse(dto) });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -161,23 +95,16 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
// ===================================================================================
|
||||
[HttpDelete]
|
||||
[Route("DeleteEvent/{id}")]
|
||||
public async Task<IActionResult> DeleteEvent(int id)
|
||||
public async Task<IActionResult> DeleteEvent(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var model = await _db.Events.FindAsync(id);
|
||||
if (model == null)
|
||||
var deleted = await _calendarService.DeleteEventAsync(id, cancellationToken);
|
||||
if (!deleted)
|
||||
{
|
||||
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)
|
||||
|
|
@ -191,58 +118,19 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
// ===================================================================================
|
||||
[HttpGet]
|
||||
[Route("GetEventList")]
|
||||
public IActionResult GetEventList(string? search = "", int page = 1, int pageSize = 10)
|
||||
public async Task<IActionResult> GetEventList(string? search = "", int page = 1, int pageSize = 10, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var query = _db.Events.Where(e => e.IsDeleted != true);
|
||||
var result = await _calendarService.GetEventListAsync(search, page, pageSize, cancellationToken);
|
||||
|
||||
// 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();
|
||||
var events = result.Rows.Select(MapToResponse).ToList();
|
||||
|
||||
return Ok(new DataResponse
|
||||
{
|
||||
Message = "Success",
|
||||
Status = "200",
|
||||
Data = new { rows = events, totalCount }
|
||||
Data = new { rows = events, totalCount = result.TotalCount }
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -256,51 +144,62 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
// ===================================================================================
|
||||
[HttpGet]
|
||||
[Route("GetEventsForCalendar")]
|
||||
public IActionResult GetEventsForCalendar([FromQuery] DateTime? start, [FromQuery] DateTime? end)
|
||||
public async Task<IActionResult> GetEventsForCalendar([FromQuery] DateTime? start, [FromQuery] DateTime? end, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var query = _db.Events.Where(e => e.IsDeleted != true);
|
||||
var events = await _calendarService.GetEventsForCalendarAsync(start, end, cancellationToken);
|
||||
|
||||
// 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 });
|
||||
return Ok(new DataResponse { Message = "Success", Status = "200", Data = events.Select(MapToResponse).ToList() });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new Response { Status = "Error", Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
private static EditEventInput MapToInput(EditEvent_DTO eventDto)
|
||||
{
|
||||
return new EditEventInput
|
||||
{
|
||||
Id = eventDto.Id,
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
private static Event_DTO MapToResponse(EventDTO dto)
|
||||
{
|
||||
return new Event_DTO
|
||||
{
|
||||
Id = dto.Id,
|
||||
Title = dto.Title,
|
||||
Description = dto.Description,
|
||||
Location = dto.Location,
|
||||
RecordID = dto.RecordID,
|
||||
RecordName = dto.RecordName,
|
||||
RecordTargetType = dto.RecordTargetType,
|
||||
ReminderMinutes = dto.ReminderMinutes,
|
||||
EventColor = dto.EventColor,
|
||||
StartDate = dto.StartDate,
|
||||
StartTime = dto.StartTime,
|
||||
EndDate = dto.EndDate,
|
||||
EndTime = dto.EndTime,
|
||||
AllDay = dto.AllDay,
|
||||
CreatedDate = dto.CreatedDate,
|
||||
createdby = dto.createdby
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,73 +1,49 @@
|
|||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Api.SeaHavenIndustries.Helper.ViewModel;
|
||||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
|
||||
|
||||
public class CommonController : Controller
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly ApplicationDbContext _db;
|
||||
public CommonController(UserManager<ApplicationUser> userManager, ApplicationDbContext db)
|
||||
private readonly ICommonService _commonService;
|
||||
|
||||
public CommonController(ICommonService commonService)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_db = db;
|
||||
_commonService = commonService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("GetUsers")]
|
||||
public async Task<IActionResult> GetUsers(string? search = "", int page = 1, int pageSize = 10)
|
||||
public async Task<IActionResult> GetUsers(string? search = "", int page = 1, int pageSize = 10, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var allUsersQuery = _db.Users
|
||||
.Where(w => (w.FirstName ?? "").ToLower().Contains((search ?? "").ToLower()))
|
||||
.OrderByDescending(d => d.Id);
|
||||
var result = await _commonService.GetUsersPagedAsync(search, page, pageSize, cancellationToken);
|
||||
|
||||
var totalCount = allUsersQuery.Count();
|
||||
|
||||
var paginatedUsers = allUsersQuery
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToList();
|
||||
// Calculate the number of pages
|
||||
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
|
||||
|
||||
var filteredUsers = new List<UserRoleViewModel>();
|
||||
|
||||
foreach (var user in paginatedUsers)
|
||||
var filteredUsers = result.Users.Select(u => new UserRoleViewModel
|
||||
{
|
||||
var roles = await _userManager.GetRolesAsync(user);
|
||||
if (!roles.Contains("Admin"))
|
||||
{
|
||||
filteredUsers.Add(new UserRoleViewModel
|
||||
{
|
||||
RoleName = roles.Count()>0?roles.First():"",
|
||||
Email = user.Email,
|
||||
UserName = user.FirstName,
|
||||
Date = user.CreatedDate?.Date.ToString(),
|
||||
Status = user.UniqueName,
|
||||
Id = user.Id
|
||||
});
|
||||
}
|
||||
}
|
||||
// Create a view model containing the paged items and pagination metadata
|
||||
RoleName = u.RoleName,
|
||||
Email = u.Email,
|
||||
UserName = u.UserName,
|
||||
Date = u.Date,
|
||||
Status = u.Status,
|
||||
Id = u.Id
|
||||
}).ToList();
|
||||
|
||||
var viewModel = new Pagination_DTO
|
||||
{
|
||||
Data = filteredUsers,
|
||||
PageNumber = page,
|
||||
PageSize = pageSize,
|
||||
TotalCount = totalCount,
|
||||
TotalPages = totalPages
|
||||
PageNumber = result.PageNumber,
|
||||
PageSize = result.PageSize,
|
||||
TotalCount = result.TotalCount,
|
||||
TotalPages = result.TotalPages
|
||||
};
|
||||
return Ok(viewModel);
|
||||
}
|
||||
|
|
@ -79,12 +55,12 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
|
||||
[HttpGet]
|
||||
[Route("GetRoles")]
|
||||
public async Task<IActionResult> GetRoles()
|
||||
public async Task<IActionResult> GetRoles(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = await _db.Roles.Select(s => new {Id=s.Name,s.Name}).ToListAsync();
|
||||
|
||||
var data = await _commonService.GetRolesAsync(cancellationToken);
|
||||
|
||||
return Ok(data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -96,41 +72,26 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
|
||||
[HttpGet]
|
||||
[Route("GetSitesName")]
|
||||
public async Task<IActionResult> GetSiteName()
|
||||
public async Task<IActionResult> GetSiteName(CancellationToken cancellationToken)
|
||||
{
|
||||
var data =await _db.Locations.Select(n=> new
|
||||
{
|
||||
n.Id,
|
||||
n.Name,
|
||||
n.Title
|
||||
}).ToListAsync();
|
||||
var data = await _commonService.GetSiteNamesAsync(cancellationToken);
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("GetContacts")]
|
||||
public async Task<IActionResult> GetContacts()
|
||||
public async Task<IActionResult> GetContacts(CancellationToken cancellationToken)
|
||||
{
|
||||
var data =await _db.Contacts.Select(n=> new
|
||||
{
|
||||
n.Id,
|
||||
Name = (n.FirstName + " " + n.LastName).Trim(),
|
||||
email=n.Email,
|
||||
phone=n.PhoneNumber
|
||||
}).ToListAsync();
|
||||
var data = await _commonService.GetContactsAsync(cancellationToken);
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("GetCategories")]
|
||||
public async Task<IActionResult> GetCategories()
|
||||
public async Task<IActionResult> GetCategories(CancellationToken cancellationToken)
|
||||
{
|
||||
var data = await _db.Categories.Select(s=> new
|
||||
{
|
||||
s.Id,
|
||||
s.Name
|
||||
}).ToListAsync();
|
||||
var data = await _commonService.GetCategoriesAsync(cancellationToken);
|
||||
return Ok(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
|
|
@ -10,29 +9,24 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
[Route("api/[controller]")]
|
||||
public class DashboardController : Controller
|
||||
{
|
||||
private readonly ApplicationDbContext _db;
|
||||
private readonly IDashboardService _dashboardService;
|
||||
|
||||
public DashboardController(ApplicationDbContext db)
|
||||
public DashboardController(IDashboardService dashboardService)
|
||||
{
|
||||
_db = db;
|
||||
_dashboardService = dashboardService;
|
||||
}
|
||||
|
||||
[HttpGet("Stats")]
|
||||
public async Task<IActionResult> GetStats()
|
||||
public async Task<IActionResult> GetStats(CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _db.workOrders.Where(w => w.istemplate != true);
|
||||
|
||||
var total = await query.CountAsync();
|
||||
var open = await query.CountAsync(w => w.Status == "Open" || w.Status == "In Progress" || w.Status == "On Hold");
|
||||
var notDispatched = await query.CountAsync(w => (w.Status == "Open") && (w.AssignTo == null || w.AssignTo == ""));
|
||||
var completed = await query.CountAsync(w => w.Status == "Done");
|
||||
var stats = await _dashboardService.GetStatsAsync(cancellationToken);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
total,
|
||||
open,
|
||||
notDispatched,
|
||||
completed
|
||||
total = stats.Total,
|
||||
open = stats.Open,
|
||||
notDispatched = stats.NotDispatched,
|
||||
completed = stats.Completed
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Api.SeaHavenIndustries.DTOs;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
|
|
@ -10,78 +11,80 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
[Route("api/[controller]")]
|
||||
public class DropdownOptionsController : Controller
|
||||
{
|
||||
private readonly ApplicationDbContext _db;
|
||||
private readonly IDropdownOptionsService _dropdownOptionsService;
|
||||
|
||||
public DropdownOptionsController(ApplicationDbContext db)
|
||||
public DropdownOptionsController(IDropdownOptionsService dropdownOptionsService)
|
||||
{
|
||||
_db = db;
|
||||
_dropdownOptionsService = dropdownOptionsService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetAll([FromQuery] string? category = null)
|
||||
public async Task<IActionResult> GetAll([FromQuery] string? category = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = _db.DropdownOptions.Where(d => d.IsActive);
|
||||
if (!string.IsNullOrWhiteSpace(category))
|
||||
query = query.Where(d => d.Category == category);
|
||||
|
||||
var data = await query.OrderBy(d => d.Category).ThenBy(d => d.SortOrder).ThenBy(d => d.Value).ToListAsync();
|
||||
var data = await _dropdownOptionsService.GetAllAsync(category, cancellationToken);
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
[HttpGet("ByCategory/{category}")]
|
||||
public async Task<IActionResult> GetByCategory(string category, [FromQuery] string? parent = null)
|
||||
public async Task<IActionResult> GetByCategory(string category, [FromQuery] string? parent = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = _db.DropdownOptions.Where(d => d.IsActive && d.Category == category);
|
||||
if (!string.IsNullOrWhiteSpace(parent))
|
||||
query = query.Where(d => d.ParentValue == parent);
|
||||
|
||||
var data = await query.OrderBy(d => d.SortOrder).ThenBy(d => d.Value).ToListAsync();
|
||||
var data = await _dropdownOptionsService.GetByCategoryAsync(category, parent, cancellationToken);
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Create([FromBody] DropdownOption model)
|
||||
public async Task<IActionResult> Create([FromBody] DropdownOption_DTO model, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(model.Category) || string.IsNullOrWhiteSpace(model.Value))
|
||||
return BadRequest(new { message = "Category and Value are required" });
|
||||
|
||||
var existing = await _db.DropdownOptions
|
||||
.AnyAsync(d => d.Category == model.Category && d.Value == model.Value && d.ParentValue == model.ParentValue);
|
||||
if (existing)
|
||||
return BadRequest(new { message = "This option already exists" });
|
||||
|
||||
model.Id = 0;
|
||||
model.IsActive = true;
|
||||
_db.DropdownOptions.Add(model);
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(model);
|
||||
try
|
||||
{
|
||||
var created = await _dropdownOptionsService.CreateAsync(MapToService(model), cancellationToken);
|
||||
return Ok(created);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return BadRequest(new { message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> Update(int id, [FromBody] DropdownOption model)
|
||||
public async Task<IActionResult> Update(int id, [FromBody] DropdownOption_DTO model, CancellationToken cancellationToken)
|
||||
{
|
||||
var item = await _db.DropdownOptions.FindAsync(id);
|
||||
if (item == null)
|
||||
try
|
||||
{
|
||||
var updated = await _dropdownOptionsService.UpdateAsync(id, MapToService(model), cancellationToken);
|
||||
return Ok(updated);
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new { message = "Option not found" });
|
||||
|
||||
item.Value = model.Value;
|
||||
item.ParentValue = model.ParentValue;
|
||||
item.SortOrder = model.SortOrder;
|
||||
item.IsActive = model.IsActive;
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(item);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
public async Task<IActionResult> Delete(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var item = await _db.DropdownOptions.FindAsync(id);
|
||||
if (item == null)
|
||||
try
|
||||
{
|
||||
await _dropdownOptionsService.DeleteAsync(id, cancellationToken);
|
||||
return Ok(new { message = "Deleted" });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
return NotFound(new { message = "Option not found" });
|
||||
}
|
||||
}
|
||||
|
||||
_db.DropdownOptions.Remove(item);
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new { message = "Deleted" });
|
||||
private static DropdownOptionDTO MapToService(DropdownOption_DTO model)
|
||||
{
|
||||
return new DropdownOptionDTO
|
||||
{
|
||||
Id = model.Id,
|
||||
Category = model.Category,
|
||||
Value = model.Value,
|
||||
ParentValue = model.ParentValue,
|
||||
SortOrder = model.SortOrder,
|
||||
IsActive = model.IsActive
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ using Api.SeaHavenIndustries.DTOs;
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
|
||||
namespace Api.SeaHavenIndustries.Controllers
|
||||
{
|
||||
|
|
@ -12,57 +13,24 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
[Route("api/tasklisttemplates")]
|
||||
public class TaskListTemplateController : Controller
|
||||
{
|
||||
private readonly ApplicationDbContext _db;
|
||||
private readonly ITaskListTemplateService _taskListTemplateService;
|
||||
|
||||
public TaskListTemplateController(ApplicationDbContext db)
|
||||
public TaskListTemplateController(ITaskListTemplateService taskListTemplateService)
|
||||
{
|
||||
_db = db;
|
||||
_taskListTemplateService = taskListTemplateService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetAll()
|
||||
public async Task<IActionResult> GetAll(CancellationToken cancellationToken)
|
||||
{
|
||||
var data = await _db.TaskListTemplates
|
||||
.Where(t => t.IsActive)
|
||||
.Include(t => t.Items.OrderBy(i => i.SortOrder))
|
||||
.OrderBy(t => t.Name)
|
||||
.Select(t => new
|
||||
{
|
||||
t.Id,
|
||||
t.Name,
|
||||
t.Description,
|
||||
t.IsActive,
|
||||
items = t.Items.OrderBy(i => i.SortOrder).Select(i => new
|
||||
{
|
||||
i.Id,
|
||||
i.ItemText,
|
||||
i.SortOrder
|
||||
}).ToList()
|
||||
})
|
||||
.ToListAsync();
|
||||
var data = await _taskListTemplateService.GetAllAsync(cancellationToken);
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
public async Task<IActionResult> GetById(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var template = await _db.TaskListTemplates
|
||||
.Include(t => t.Items.OrderBy(i => i.SortOrder))
|
||||
.Where(t => t.Id == id)
|
||||
.Select(t => new
|
||||
{
|
||||
t.Id,
|
||||
t.Name,
|
||||
t.Description,
|
||||
t.IsActive,
|
||||
items = t.Items.OrderBy(i => i.SortOrder).Select(i => new
|
||||
{
|
||||
i.Id,
|
||||
i.ItemText,
|
||||
i.SortOrder
|
||||
}).ToList()
|
||||
})
|
||||
.FirstOrDefaultAsync();
|
||||
var template = await _taskListTemplateService.GetByIdAsync(id, cancellationToken);
|
||||
|
||||
if (template == null)
|
||||
return NotFound(new Response { Status = "Error", Message = "Template not found" });
|
||||
|
|
@ -71,25 +39,13 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Create([FromBody] TaskListTemplate_DTO model)
|
||||
public async Task<IActionResult> Create([FromBody] TaskListTemplate_DTO model, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var template = new TaskListTemplate
|
||||
{
|
||||
Name = model.Name,
|
||||
Description = model.Description,
|
||||
IsActive = model.IsActive,
|
||||
Items = model.Items.Select((item, idx) => new TaskListTemplateItem
|
||||
{
|
||||
ItemText = item.ItemText,
|
||||
SortOrder = item.SortOrder > 0 ? item.SortOrder : idx
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
_db.TaskListTemplates.Add(template);
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new { message = "Template created", id = template.Id });
|
||||
var input = MapToInput(model);
|
||||
var id = await _taskListTemplateService.CreateAsync(input, cancellationToken);
|
||||
return Ok(new { message = "Template created", id });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -98,31 +54,15 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> Update(int id, [FromBody] TaskListTemplate_DTO model)
|
||||
public async Task<IActionResult> Update(int id, [FromBody] TaskListTemplate_DTO model, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var template = await _db.TaskListTemplates
|
||||
.Include(t => t.Items)
|
||||
.FirstOrDefaultAsync(t => t.Id == id);
|
||||
|
||||
if (template == null)
|
||||
var input = MapToInput(model);
|
||||
var updated = await _taskListTemplateService.UpdateAsync(id, input, cancellationToken);
|
||||
if (!updated)
|
||||
return NotFound(new Response { Status = "Error", Message = "Template not found" });
|
||||
|
||||
template.Name = model.Name;
|
||||
template.Description = model.Description;
|
||||
template.IsActive = model.IsActive;
|
||||
|
||||
_db.TaskListTemplateItems.RemoveRange(template.Items);
|
||||
|
||||
template.Items = model.Items.Select((item, idx) => new TaskListTemplateItem
|
||||
{
|
||||
TaskListTemplateId = id,
|
||||
ItemText = item.ItemText,
|
||||
SortOrder = item.SortOrder > 0 ? item.SortOrder : idx
|
||||
}).ToList();
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new { message = "Template updated" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -132,15 +72,28 @@ namespace Api.SeaHavenIndustries.Controllers
|
|||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
public async Task<IActionResult> Delete(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var template = await _db.TaskListTemplates.FindAsync(id);
|
||||
if (template == null)
|
||||
var deleted = await _taskListTemplateService.DeleteAsync(id, cancellationToken);
|
||||
if (!deleted)
|
||||
return NotFound(new Response { Status = "Error", Message = "Template not found" });
|
||||
|
||||
template.IsActive = false;
|
||||
await _db.SaveChangesAsync();
|
||||
return Ok(new { message = "Template deleted" });
|
||||
}
|
||||
|
||||
private static TaskListTemplateInput MapToInput(TaskListTemplate_DTO model)
|
||||
{
|
||||
return new TaskListTemplateInput
|
||||
{
|
||||
Name = model.Name,
|
||||
Description = model.Description,
|
||||
IsActive = model.IsActive,
|
||||
Items = model.Items.Select(item => new TaskListTemplateInputItem
|
||||
{
|
||||
ItemText = item.ItemText,
|
||||
SortOrder = item.SortOrder
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
12
Api.SeaHavenIndustries/DTOs/DropdownOption_DTO.cs
Normal file
12
Api.SeaHavenIndustries/DTOs/DropdownOption_DTO.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
namespace Api.SeaHavenIndustries.DTOs
|
||||
{
|
||||
public class DropdownOption_DTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Category { get; set; } = "";
|
||||
public string Value { get; set; } = "";
|
||||
public string? ParentValue { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
}
|
||||
}
|
||||
85
SeaHaven.DataServices/Implementation/CalendarDataService.cs
Normal file
85
SeaHaven.DataServices/Implementation/CalendarDataService.cs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
|
||||
namespace SeaHaven.DataServices.Implementation
|
||||
{
|
||||
public class CalendarDataService : ICalendarDataService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public CalendarDataService(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Events?> GetEventByIdAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _context.Events
|
||||
.AsNoTracking()
|
||||
.Where(e => e.Id == id && e.IsDeleted != true)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<Events?> GetEventForEditAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _context.Events.FindAsync(new object[] { id }, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<Events> AddEventAsync(Events ev, CancellationToken cancellationToken)
|
||||
{
|
||||
await _context.Events.AddAsync(ev, cancellationToken);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return ev;
|
||||
}
|
||||
|
||||
public async Task UpdateEventAsync(Events ev, CancellationToken cancellationToken)
|
||||
{
|
||||
_context.Events.Update(ev);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<(IEnumerable<Events> Items, int TotalCount)> GetEventListPagedAsync(int page, int pageSize, string? search, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.Events.Where(e => e.IsDeleted != true);
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
var totalCount = await query.CountAsync(cancellationToken);
|
||||
|
||||
var events = await query
|
||||
.OrderByDescending(e => e.StartDate)
|
||||
.ThenByDescending(e => e.CreatedDate)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.AsNoTracking()
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return (events, totalCount);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Events>> GetEventsForCalendarAsync(DateTime? start, DateTime? end, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.Events.Where(e => e.IsDeleted != true);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
return await query
|
||||
.AsNoTracking()
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
81
SeaHaven.DataServices/Implementation/CommonDataService.cs
Normal file
81
SeaHaven.DataServices/Implementation/CommonDataService.cs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
|
||||
namespace SeaHaven.DataServices.Implementation
|
||||
{
|
||||
public class CommonDataService : ICommonDataService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public CommonDataService(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<(IEnumerable<ApplicationUser> Items, int TotalCount)> GetUsersPagedAsync(int page, int pageSize, string search, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.Users
|
||||
.Where(w => (w.FirstName ?? "").ToLower().Contains((search ?? "").ToLower()))
|
||||
.OrderByDescending(d => d.Id);
|
||||
|
||||
var totalCount = await query.CountAsync(cancellationToken);
|
||||
|
||||
var paginatedUsers = await query
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.AsNoTracking()
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return (paginatedUsers, totalCount);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<string?>> GetRoleNamesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return await _context.Roles
|
||||
.AsNoTracking()
|
||||
.Select(s => s.Name)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Locations>> GetSiteNamesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return await _context.Locations
|
||||
.AsNoTracking()
|
||||
.Select(n => new Locations
|
||||
{
|
||||
Id = n.Id,
|
||||
Name = n.Name,
|
||||
Title = n.Title
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Contacts>> GetContactsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return await _context.Contacts
|
||||
.AsNoTracking()
|
||||
.Select(n => new Contacts
|
||||
{
|
||||
Id = n.Id,
|
||||
FirstName = n.FirstName,
|
||||
LastName = n.LastName,
|
||||
Email = n.Email,
|
||||
PhoneNumber = n.PhoneNumber
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Category>> GetCategoriesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return await _context.Categories
|
||||
.AsNoTracking()
|
||||
.Select(s => new Category
|
||||
{
|
||||
Id = s.Id,
|
||||
Name = s.Name
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
34
SeaHaven.DataServices/Implementation/DashboardDataService.cs
Normal file
34
SeaHaven.DataServices/Implementation/DashboardDataService.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
|
||||
namespace SeaHaven.DataServices.Implementation
|
||||
{
|
||||
public class DashboardDataService : IDashboardDataService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public DashboardDataService(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<DashboardCounts> GetWorkOrderCountsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.workOrders.Where(w => w.istemplate != true);
|
||||
|
||||
var total = await query.CountAsync(cancellationToken);
|
||||
var open = await query.CountAsync(w => w.Status == "Open" || w.Status == "In Progress" || w.Status == "On Hold", cancellationToken);
|
||||
var notDispatched = await query.CountAsync(w => (w.Status == "Open") && (w.AssignTo == null || w.AssignTo == ""), cancellationToken);
|
||||
var completed = await query.CountAsync(w => w.Status == "Done", cancellationToken);
|
||||
|
||||
return new DashboardCounts
|
||||
{
|
||||
Total = total,
|
||||
Open = open,
|
||||
NotDispatched = notDispatched,
|
||||
Completed = completed
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
|
||||
namespace SeaHaven.DataServices.Implementation
|
||||
{
|
||||
public class DropdownOptionsDataService : IDropdownOptionsDataService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public DropdownOptionsDataService(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<DropdownOption>> GetAllActiveAsync(string? category, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.DropdownOptions.Where(d => d.IsActive);
|
||||
if (!string.IsNullOrWhiteSpace(category))
|
||||
query = query.Where(d => d.Category == category);
|
||||
|
||||
return await query
|
||||
.AsNoTracking()
|
||||
.OrderBy(d => d.Category).ThenBy(d => d.SortOrder).ThenBy(d => d.Value)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<DropdownOption>> GetByCategoryAsync(string category, string? parent, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = _context.DropdownOptions.Where(d => d.IsActive && d.Category == category);
|
||||
if (!string.IsNullOrWhiteSpace(parent))
|
||||
query = query.Where(d => d.ParentValue == parent);
|
||||
|
||||
return await query
|
||||
.AsNoTracking()
|
||||
.OrderBy(d => d.SortOrder).ThenBy(d => d.Value)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<bool> ExistsAsync(string category, string value, string? parentValue, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _context.DropdownOptions
|
||||
.AsNoTracking()
|
||||
.AnyAsync(d => d.Category == category && d.Value == value && d.ParentValue == parentValue, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<DropdownOption> AddAsync(DropdownOption option, CancellationToken cancellationToken)
|
||||
{
|
||||
await _context.DropdownOptions.AddAsync(option, cancellationToken);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return option;
|
||||
}
|
||||
|
||||
public async Task<DropdownOption?> GetByIdAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _context.DropdownOptions.FindAsync(new object[] { id }, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(DropdownOption option, CancellationToken cancellationToken)
|
||||
{
|
||||
_context.DropdownOptions.Update(option);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task RemoveAsync(DropdownOption option, CancellationToken cancellationToken)
|
||||
{
|
||||
_context.DropdownOptions.Remove(option);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -57,5 +57,58 @@ namespace SeaHaven.DataServices.Implementation
|
|||
{
|
||||
return await _context.TaskListTemplates.AnyAsync(t => t.Id == id);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TaskListTemplate>> GetActiveTemplatesWithItemsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return await _context.TaskListTemplates
|
||||
.Include(t => t.Items)
|
||||
.Where(t => t.IsActive)
|
||||
.OrderBy(t => t.Name)
|
||||
.AsNoTracking()
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<TaskListTemplate?> GetTemplateWithItemsReadOnlyAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _context.TaskListTemplates
|
||||
.Include(t => t.Items)
|
||||
.Where(t => t.Id == id)
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<TaskListTemplate?> GetTemplateWithItemsForUpdateAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _context.TaskListTemplates
|
||||
.Include(t => t.Items)
|
||||
.FirstOrDefaultAsync(t => t.Id == id, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<TaskListTemplate> AddTemplateAsync(TaskListTemplate template, CancellationToken cancellationToken)
|
||||
{
|
||||
await _context.TaskListTemplates.AddAsync(template, cancellationToken);
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return template;
|
||||
}
|
||||
|
||||
public async Task ReplaceTemplateItemsAsync(TaskListTemplate template, IEnumerable<TaskListTemplateItem> newItems, CancellationToken cancellationToken)
|
||||
{
|
||||
if (template.Items != null)
|
||||
_context.TaskListTemplateItems.RemoveRange(template.Items);
|
||||
|
||||
template.Items = newItems.ToList();
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<bool> DeactivateTemplateAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var template = await _context.TaskListTemplates.FindAsync(new object[] { id }, cancellationToken);
|
||||
if (template == null)
|
||||
return false;
|
||||
|
||||
template.IsActive = false;
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
14
SeaHaven.DataServices/Interfaces/ICalendarDataService.cs
Normal file
14
SeaHaven.DataServices/Interfaces/ICalendarDataService.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
|
||||
namespace SeaHaven.DataServices.Interfaces
|
||||
{
|
||||
public interface ICalendarDataService
|
||||
{
|
||||
Task<Events?> GetEventByIdAsync(int id, CancellationToken cancellationToken);
|
||||
Task<Events?> GetEventForEditAsync(int id, CancellationToken cancellationToken);
|
||||
Task<Events> AddEventAsync(Events ev, CancellationToken cancellationToken);
|
||||
Task UpdateEventAsync(Events ev, CancellationToken cancellationToken);
|
||||
Task<(IEnumerable<Events> Items, int TotalCount)> GetEventListPagedAsync(int page, int pageSize, string? search, CancellationToken cancellationToken);
|
||||
Task<IEnumerable<Events>> GetEventsForCalendarAsync(DateTime? start, DateTime? end, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
13
SeaHaven.DataServices/Interfaces/ICommonDataService.cs
Normal file
13
SeaHaven.DataServices/Interfaces/ICommonDataService.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
|
||||
namespace SeaHaven.DataServices.Interfaces
|
||||
{
|
||||
public interface ICommonDataService
|
||||
{
|
||||
Task<(IEnumerable<ApplicationUser> Items, int TotalCount)> GetUsersPagedAsync(int page, int pageSize, string search, CancellationToken cancellationToken);
|
||||
Task<IEnumerable<string?>> GetRoleNamesAsync(CancellationToken cancellationToken);
|
||||
Task<IEnumerable<Locations>> GetSiteNamesAsync(CancellationToken cancellationToken);
|
||||
Task<IEnumerable<Contacts>> GetContactsAsync(CancellationToken cancellationToken);
|
||||
Task<IEnumerable<Category>> GetCategoriesAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
15
SeaHaven.DataServices/Interfaces/IDashboardDataService.cs
Normal file
15
SeaHaven.DataServices/Interfaces/IDashboardDataService.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
namespace SeaHaven.DataServices.Interfaces
|
||||
{
|
||||
public interface IDashboardDataService
|
||||
{
|
||||
Task<DashboardCounts> GetWorkOrderCountsAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public class DashboardCounts
|
||||
{
|
||||
public int Total { get; set; }
|
||||
public int Open { get; set; }
|
||||
public int NotDispatched { get; set; }
|
||||
public int Completed { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
|
||||
namespace SeaHaven.DataServices.Interfaces
|
||||
{
|
||||
public interface IDropdownOptionsDataService
|
||||
{
|
||||
Task<IEnumerable<DropdownOption>> GetAllActiveAsync(string? category, CancellationToken cancellationToken);
|
||||
Task<IEnumerable<DropdownOption>> GetByCategoryAsync(string category, string? parent, CancellationToken cancellationToken);
|
||||
Task<bool> ExistsAsync(string category, string value, string? parentValue, CancellationToken cancellationToken);
|
||||
Task<DropdownOption> AddAsync(DropdownOption option, CancellationToken cancellationToken);
|
||||
Task<DropdownOption?> GetByIdAsync(int id, CancellationToken cancellationToken);
|
||||
Task UpdateAsync(DropdownOption option, CancellationToken cancellationToken);
|
||||
Task RemoveAsync(DropdownOption option, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,5 +11,13 @@ namespace SeaHaven.DataServices.Interfaces
|
|||
Task UpdateAsync(TaskListTemplate template);
|
||||
Task DeleteAsync(int id);
|
||||
Task<bool> ExistsAsync(int id);
|
||||
|
||||
// Cancelable query/command operations used by the TaskListTemplate service layer
|
||||
Task<IEnumerable<TaskListTemplate>> GetActiveTemplatesWithItemsAsync(CancellationToken cancellationToken);
|
||||
Task<TaskListTemplate?> GetTemplateWithItemsReadOnlyAsync(int id, CancellationToken cancellationToken);
|
||||
Task<TaskListTemplate?> GetTemplateWithItemsForUpdateAsync(int id, CancellationToken cancellationToken);
|
||||
Task<TaskListTemplate> AddTemplateAsync(TaskListTemplate template, CancellationToken cancellationToken);
|
||||
Task ReplaceTemplateItemsAsync(TaskListTemplate template, IEnumerable<TaskListTemplateItem> newItems, CancellationToken cancellationToken);
|
||||
Task<bool> DeactivateTemplateAsync(int id, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
50
SeaHaven.Services/DTOs/CalendarDTOs.cs
Normal file
50
SeaHaven.Services/DTOs/CalendarDTOs.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
namespace SeaHaven.Services.DTOs
|
||||
{
|
||||
public class EventDTO
|
||||
{
|
||||
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; }
|
||||
}
|
||||
|
||||
public class PagedEventsResult
|
||||
{
|
||||
public List<EventDTO> Rows { get; set; } = new();
|
||||
public int TotalCount { get; set; }
|
||||
}
|
||||
|
||||
public class EditEventInput
|
||||
{
|
||||
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; }
|
||||
}
|
||||
}
|
||||
48
SeaHaven.Services/DTOs/CommonDTOs.cs
Normal file
48
SeaHaven.Services/DTOs/CommonDTOs.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
namespace SeaHaven.Services.DTOs
|
||||
{
|
||||
public class UserListItemDTO
|
||||
{
|
||||
public string? Id { get; set; }
|
||||
public string? UserName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? RoleName { get; set; }
|
||||
public string? Status { get; set; }
|
||||
public string? Date { get; set; }
|
||||
}
|
||||
|
||||
public class PagedUsersResult
|
||||
{
|
||||
public List<UserListItemDTO> Users { get; set; } = new();
|
||||
public int PageNumber { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public int TotalCount { get; set; }
|
||||
public int TotalPages { get; set; }
|
||||
}
|
||||
|
||||
public class RoleListItemDTO
|
||||
{
|
||||
public string? Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
}
|
||||
|
||||
public class SiteNameDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Title { get; set; }
|
||||
}
|
||||
|
||||
public class ContactListItemDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
}
|
||||
|
||||
public class CategoryListItemDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
}
|
||||
}
|
||||
10
SeaHaven.Services/DTOs/DashboardDTOs.cs
Normal file
10
SeaHaven.Services/DTOs/DashboardDTOs.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
namespace SeaHaven.Services.DTOs
|
||||
{
|
||||
public class DashboardStatsDTO
|
||||
{
|
||||
public int Total { get; set; }
|
||||
public int Open { get; set; }
|
||||
public int NotDispatched { get; set; }
|
||||
public int Completed { get; set; }
|
||||
}
|
||||
}
|
||||
12
SeaHaven.Services/DTOs/DropdownOptionDTOs.cs
Normal file
12
SeaHaven.Services/DTOs/DropdownOptionDTOs.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
namespace SeaHaven.Services.DTOs
|
||||
{
|
||||
public class DropdownOptionDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Category { get; set; } = "";
|
||||
public string Value { get; set; } = "";
|
||||
public string? ParentValue { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
}
|
||||
}
|
||||
32
SeaHaven.Services/DTOs/TaskListTemplateDTOs.cs
Normal file
32
SeaHaven.Services/DTOs/TaskListTemplateDTOs.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
namespace SeaHaven.Services.DTOs
|
||||
{
|
||||
public class TaskListTemplateDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
public List<TaskListTemplateItemDTO> Items { get; set; } = new();
|
||||
}
|
||||
|
||||
public class TaskListTemplateItemDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string? ItemText { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
}
|
||||
|
||||
public class TaskListTemplateInput
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public List<TaskListTemplateInputItem> Items { get; set; } = new();
|
||||
}
|
||||
|
||||
public class TaskListTemplateInputItem
|
||||
{
|
||||
public string? ItemText { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
}
|
||||
}
|
||||
125
SeaHaven.Services/Implementation/CalendarService.cs
Normal file
125
SeaHaven.Services/Implementation/CalendarService.cs
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
|
||||
namespace SeaHaven.Services.Implementation
|
||||
{
|
||||
public class CalendarService : ICalendarService
|
||||
{
|
||||
private readonly ICalendarDataService _dataService;
|
||||
|
||||
public CalendarService(ICalendarDataService dataService)
|
||||
{
|
||||
_dataService = dataService;
|
||||
}
|
||||
|
||||
public async Task CreateEventAsync(EditEventInput input, CancellationToken cancellationToken)
|
||||
{
|
||||
var model = new Events
|
||||
{
|
||||
Title = input.Title,
|
||||
Description = input.Description,
|
||||
Location = input.Location,
|
||||
RecordID = input.RecordID,
|
||||
RecordName = input.RecordName,
|
||||
RecordTargetType = input.RecordTargetType,
|
||||
ReminderMinutes = input.ReminderMinutes,
|
||||
EventColor = input.EventColor,
|
||||
StartDate = input.StartDate,
|
||||
StartTime = input.StartTime,
|
||||
EndDate = input.EndDate,
|
||||
EndTime = input.EndTime,
|
||||
AllDay = input.AllDay,
|
||||
CreatedDate = DateTime.Now,
|
||||
IsDeleted = false
|
||||
};
|
||||
|
||||
await _dataService.AddEventAsync(model, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateEventAsync(EditEventInput input, CancellationToken cancellationToken)
|
||||
{
|
||||
var model = await _dataService.GetEventForEditAsync(input.Id!.Value, cancellationToken);
|
||||
if (model == null || model.IsDeleted == true)
|
||||
return false;
|
||||
|
||||
model.Title = input.Title;
|
||||
model.Description = input.Description;
|
||||
model.Location = input.Location;
|
||||
model.RecordID = input.RecordID;
|
||||
model.RecordName = input.RecordName;
|
||||
model.RecordTargetType = input.RecordTargetType;
|
||||
model.ReminderMinutes = input.ReminderMinutes;
|
||||
model.EventColor = input.EventColor;
|
||||
model.StartDate = input.StartDate;
|
||||
model.StartTime = input.StartTime;
|
||||
model.EndDate = input.EndDate;
|
||||
model.EndTime = input.EndTime;
|
||||
model.AllDay = input.AllDay;
|
||||
model.LastModificationTime = DateTime.Now;
|
||||
|
||||
await _dataService.UpdateEventAsync(model, cancellationToken);
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<EventDTO?> GetEventByIdAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var model = await _dataService.GetEventByIdAsync(id, cancellationToken);
|
||||
return model == null ? null : MapToDTO(model);
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteEventAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var model = await _dataService.GetEventForEditAsync(id, cancellationToken);
|
||||
if (model == null)
|
||||
return false;
|
||||
|
||||
model.IsDeleted = true;
|
||||
model.DeletionTime = DateTime.Now;
|
||||
|
||||
await _dataService.UpdateEventAsync(model, cancellationToken);
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<PagedEventsResult> GetEventListAsync(string? search, int page, int pageSize, CancellationToken cancellationToken)
|
||||
{
|
||||
var (items, totalCount) = await _dataService.GetEventListPagedAsync(page, pageSize, search, cancellationToken);
|
||||
|
||||
return new PagedEventsResult
|
||||
{
|
||||
Rows = items.Select(MapToDTO).ToList(),
|
||||
TotalCount = totalCount
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<EventDTO>> GetEventsForCalendarAsync(DateTime? start, DateTime? end, CancellationToken cancellationToken)
|
||||
{
|
||||
var events = await _dataService.GetEventsForCalendarAsync(start, end, cancellationToken);
|
||||
return events.Select(MapToDTO);
|
||||
}
|
||||
|
||||
private static EventDTO MapToDTO(Events model)
|
||||
{
|
||||
return new EventDTO
|
||||
{
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
85
SeaHaven.Services/Implementation/CommonService.cs
Normal file
85
SeaHaven.Services/Implementation/CommonService.cs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
|
||||
namespace SeaHaven.Services.Implementation
|
||||
{
|
||||
public class CommonService : ICommonService
|
||||
{
|
||||
private readonly ICommonDataService _dataService;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
public CommonService(ICommonDataService dataService, UserManager<ApplicationUser> userManager)
|
||||
{
|
||||
_dataService = dataService;
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
public async Task<PagedUsersResult> GetUsersPagedAsync(string? search, int page, int pageSize, CancellationToken cancellationToken)
|
||||
{
|
||||
var (paginatedUsers, totalCount) = await _dataService.GetUsersPagedAsync(page, pageSize, search ?? "", cancellationToken);
|
||||
|
||||
var filteredUsers = new List<UserListItemDTO>();
|
||||
|
||||
foreach (var user in paginatedUsers)
|
||||
{
|
||||
var roles = await _userManager.GetRolesAsync(user);
|
||||
if (!roles.Contains("Admin"))
|
||||
{
|
||||
filteredUsers.Add(new UserListItemDTO
|
||||
{
|
||||
RoleName = roles.Count() > 0 ? roles.First() : "",
|
||||
Email = user.Email,
|
||||
UserName = user.FirstName,
|
||||
Date = user.CreatedDate?.Date.ToString(),
|
||||
Status = user.UniqueName,
|
||||
Id = user.Id
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
|
||||
|
||||
return new PagedUsersResult
|
||||
{
|
||||
Users = filteredUsers,
|
||||
PageNumber = page,
|
||||
PageSize = pageSize,
|
||||
TotalCount = totalCount,
|
||||
TotalPages = totalPages
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<RoleListItemDTO>> GetRolesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var names = await _dataService.GetRoleNamesAsync(cancellationToken);
|
||||
return names.Select(name => new RoleListItemDTO { Id = name, Name = name });
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SiteNameDTO>> GetSiteNamesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var sites = await _dataService.GetSiteNamesAsync(cancellationToken);
|
||||
return sites.Select(n => new SiteNameDTO { Id = n.Id, Name = n.Name, Title = n.Title });
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ContactListItemDTO>> GetContactsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var contacts = await _dataService.GetContactsAsync(cancellationToken);
|
||||
return contacts.Select(n => new ContactListItemDTO
|
||||
{
|
||||
Id = n.Id,
|
||||
Name = (n.FirstName + " " + n.LastName).Trim(),
|
||||
Email = n.Email,
|
||||
Phone = n.PhoneNumber
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CategoryListItemDTO>> GetCategoriesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var categories = await _dataService.GetCategoriesAsync(cancellationToken);
|
||||
return categories.Select(s => new CategoryListItemDTO { Id = s.Id, Name = s.Name });
|
||||
}
|
||||
}
|
||||
}
|
||||
29
SeaHaven.Services/Implementation/DashboardService.cs
Normal file
29
SeaHaven.Services/Implementation/DashboardService.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
|
||||
namespace SeaHaven.Services.Implementation
|
||||
{
|
||||
public class DashboardService : IDashboardService
|
||||
{
|
||||
private readonly IDashboardDataService _dataService;
|
||||
|
||||
public DashboardService(IDashboardDataService dataService)
|
||||
{
|
||||
_dataService = dataService;
|
||||
}
|
||||
|
||||
public async Task<DashboardStatsDTO> GetStatsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var counts = await _dataService.GetWorkOrderCountsAsync(cancellationToken);
|
||||
|
||||
return new DashboardStatsDTO
|
||||
{
|
||||
Total = counts.Total,
|
||||
Open = counts.Open,
|
||||
NotDispatched = counts.NotDispatched,
|
||||
Completed = counts.Completed
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
88
SeaHaven.Services/Implementation/DropdownOptionsService.cs
Normal file
88
SeaHaven.Services/Implementation/DropdownOptionsService.cs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
|
||||
namespace SeaHaven.Services.Implementation
|
||||
{
|
||||
public class DropdownOptionsService : IDropdownOptionsService
|
||||
{
|
||||
private readonly IDropdownOptionsDataService _dataService;
|
||||
|
||||
public DropdownOptionsService(IDropdownOptionsDataService dataService)
|
||||
{
|
||||
_dataService = dataService;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<DropdownOptionDTO>> GetAllAsync(string? category, CancellationToken cancellationToken)
|
||||
{
|
||||
var data = await _dataService.GetAllActiveAsync(category, cancellationToken);
|
||||
return data.Select(MapToDTO);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<DropdownOptionDTO>> GetByCategoryAsync(string category, string? parent, CancellationToken cancellationToken)
|
||||
{
|
||||
var data = await _dataService.GetByCategoryAsync(category, parent, cancellationToken);
|
||||
return data.Select(MapToDTO);
|
||||
}
|
||||
|
||||
public async Task<DropdownOptionDTO> CreateAsync(DropdownOptionDTO option, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(option.Category) || string.IsNullOrWhiteSpace(option.Value))
|
||||
throw new InvalidOperationException("Category and Value are required");
|
||||
|
||||
if (await _dataService.ExistsAsync(option.Category, option.Value, option.ParentValue, cancellationToken))
|
||||
throw new InvalidOperationException("This option already exists");
|
||||
|
||||
var entity = new DropdownOption
|
||||
{
|
||||
Id = 0,
|
||||
Category = option.Category,
|
||||
Value = option.Value,
|
||||
ParentValue = option.ParentValue,
|
||||
SortOrder = option.SortOrder,
|
||||
IsActive = true
|
||||
};
|
||||
|
||||
var created = await _dataService.AddAsync(entity, cancellationToken);
|
||||
return MapToDTO(created);
|
||||
}
|
||||
|
||||
public async Task<DropdownOptionDTO> UpdateAsync(int id, DropdownOptionDTO option, CancellationToken cancellationToken)
|
||||
{
|
||||
var item = await _dataService.GetByIdAsync(id, cancellationToken);
|
||||
if (item == null)
|
||||
throw new KeyNotFoundException("Option not found");
|
||||
|
||||
item.Value = option.Value;
|
||||
item.ParentValue = option.ParentValue;
|
||||
item.SortOrder = option.SortOrder;
|
||||
item.IsActive = option.IsActive;
|
||||
|
||||
await _dataService.UpdateAsync(item, cancellationToken);
|
||||
return MapToDTO(item);
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var item = await _dataService.GetByIdAsync(id, cancellationToken);
|
||||
if (item == null)
|
||||
throw new KeyNotFoundException("Option not found");
|
||||
|
||||
await _dataService.RemoveAsync(item, cancellationToken);
|
||||
}
|
||||
|
||||
private static DropdownOptionDTO MapToDTO(DropdownOption option)
|
||||
{
|
||||
return new DropdownOptionDTO
|
||||
{
|
||||
Id = option.Id,
|
||||
Category = option.Category,
|
||||
Value = option.Value,
|
||||
ParentValue = option.ParentValue,
|
||||
SortOrder = option.SortOrder,
|
||||
IsActive = option.IsActive
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
92
SeaHaven.Services/Implementation/TaskListTemplateService.cs
Normal file
92
SeaHaven.Services/Implementation/TaskListTemplateService.cs
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
using Data.SeaHavenIndustries;
|
||||
using SeaHaven.DataServices.Interfaces;
|
||||
using SeaHaven.Services.DTOs;
|
||||
using SeaHaven.Services.Interfaces;
|
||||
|
||||
namespace SeaHaven.Services.Implementation
|
||||
{
|
||||
public class TaskListTemplateService : ITaskListTemplateService
|
||||
{
|
||||
private readonly ITaskListTemplateDataService _dataService;
|
||||
|
||||
public TaskListTemplateService(ITaskListTemplateDataService dataService)
|
||||
{
|
||||
_dataService = dataService;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TaskListTemplateDTO>> GetAllAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var templates = await _dataService.GetActiveTemplatesWithItemsAsync(cancellationToken);
|
||||
return templates.Select(MapToDTO);
|
||||
}
|
||||
|
||||
public async Task<TaskListTemplateDTO?> GetByIdAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var template = await _dataService.GetTemplateWithItemsReadOnlyAsync(id, cancellationToken);
|
||||
return template == null ? null : MapToDTO(template);
|
||||
}
|
||||
|
||||
public async Task<int> CreateAsync(TaskListTemplateInput input, CancellationToken cancellationToken)
|
||||
{
|
||||
var template = new TaskListTemplate
|
||||
{
|
||||
Name = input.Name,
|
||||
Description = input.Description,
|
||||
IsActive = input.IsActive,
|
||||
Items = input.Items.Select((item, idx) => new TaskListTemplateItem
|
||||
{
|
||||
ItemText = item.ItemText,
|
||||
SortOrder = item.SortOrder > 0 ? item.SortOrder : idx
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
var created = await _dataService.AddTemplateAsync(template, cancellationToken);
|
||||
return created.Id;
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateAsync(int id, TaskListTemplateInput input, CancellationToken cancellationToken)
|
||||
{
|
||||
var template = await _dataService.GetTemplateWithItemsForUpdateAsync(id, cancellationToken);
|
||||
if (template == null)
|
||||
return false;
|
||||
|
||||
template.Name = input.Name;
|
||||
template.Description = input.Description;
|
||||
template.IsActive = input.IsActive;
|
||||
|
||||
var newItems = input.Items.Select((item, idx) => new TaskListTemplateItem
|
||||
{
|
||||
TaskListTemplateId = id,
|
||||
ItemText = item.ItemText,
|
||||
SortOrder = item.SortOrder > 0 ? item.SortOrder : idx
|
||||
}).ToList();
|
||||
|
||||
await _dataService.ReplaceTemplateItemsAsync(template, newItems, cancellationToken);
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteAsync(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _dataService.DeactivateTemplateAsync(id, cancellationToken);
|
||||
}
|
||||
|
||||
private static TaskListTemplateDTO MapToDTO(TaskListTemplate template)
|
||||
{
|
||||
return new TaskListTemplateDTO
|
||||
{
|
||||
Id = template.Id,
|
||||
Name = template.Name,
|
||||
Description = template.Description,
|
||||
IsActive = template.IsActive,
|
||||
Items = (template.Items ?? new List<TaskListTemplateItem>())
|
||||
.OrderBy(i => i.SortOrder)
|
||||
.Select(i => new TaskListTemplateItemDTO
|
||||
{
|
||||
Id = i.Id,
|
||||
ItemText = i.ItemText,
|
||||
SortOrder = i.SortOrder
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
14
SeaHaven.Services/Interfaces/ICalendarService.cs
Normal file
14
SeaHaven.Services/Interfaces/ICalendarService.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace SeaHaven.Services.Interfaces
|
||||
{
|
||||
public interface ICalendarService
|
||||
{
|
||||
Task CreateEventAsync(EditEventInput input, CancellationToken cancellationToken);
|
||||
Task<bool> UpdateEventAsync(EditEventInput input, CancellationToken cancellationToken);
|
||||
Task<EventDTO?> GetEventByIdAsync(int id, CancellationToken cancellationToken);
|
||||
Task<bool> DeleteEventAsync(int id, CancellationToken cancellationToken);
|
||||
Task<PagedEventsResult> GetEventListAsync(string? search, int page, int pageSize, CancellationToken cancellationToken);
|
||||
Task<IEnumerable<EventDTO>> GetEventsForCalendarAsync(DateTime? start, DateTime? end, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
13
SeaHaven.Services/Interfaces/ICommonService.cs
Normal file
13
SeaHaven.Services/Interfaces/ICommonService.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace SeaHaven.Services.Interfaces
|
||||
{
|
||||
public interface ICommonService
|
||||
{
|
||||
Task<PagedUsersResult> GetUsersPagedAsync(string? search, int page, int pageSize, CancellationToken cancellationToken);
|
||||
Task<IEnumerable<RoleListItemDTO>> GetRolesAsync(CancellationToken cancellationToken);
|
||||
Task<IEnumerable<SiteNameDTO>> GetSiteNamesAsync(CancellationToken cancellationToken);
|
||||
Task<IEnumerable<ContactListItemDTO>> GetContactsAsync(CancellationToken cancellationToken);
|
||||
Task<IEnumerable<CategoryListItemDTO>> GetCategoriesAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
9
SeaHaven.Services/Interfaces/IDashboardService.cs
Normal file
9
SeaHaven.Services/Interfaces/IDashboardService.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace SeaHaven.Services.Interfaces
|
||||
{
|
||||
public interface IDashboardService
|
||||
{
|
||||
Task<DashboardStatsDTO> GetStatsAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
13
SeaHaven.Services/Interfaces/IDropdownOptionsService.cs
Normal file
13
SeaHaven.Services/Interfaces/IDropdownOptionsService.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace SeaHaven.Services.Interfaces
|
||||
{
|
||||
public interface IDropdownOptionsService
|
||||
{
|
||||
Task<IEnumerable<DropdownOptionDTO>> GetAllAsync(string? category, CancellationToken cancellationToken);
|
||||
Task<IEnumerable<DropdownOptionDTO>> GetByCategoryAsync(string category, string? parent, CancellationToken cancellationToken);
|
||||
Task<DropdownOptionDTO> CreateAsync(DropdownOptionDTO option, CancellationToken cancellationToken);
|
||||
Task<DropdownOptionDTO> UpdateAsync(int id, DropdownOptionDTO option, CancellationToken cancellationToken);
|
||||
Task DeleteAsync(int id, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
13
SeaHaven.Services/Interfaces/ITaskListTemplateService.cs
Normal file
13
SeaHaven.Services/Interfaces/ITaskListTemplateService.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
using SeaHaven.Services.DTOs;
|
||||
|
||||
namespace SeaHaven.Services.Interfaces
|
||||
{
|
||||
public interface ITaskListTemplateService
|
||||
{
|
||||
Task<IEnumerable<TaskListTemplateDTO>> GetAllAsync(CancellationToken cancellationToken);
|
||||
Task<TaskListTemplateDTO?> GetByIdAsync(int id, CancellationToken cancellationToken);
|
||||
Task<int> CreateAsync(TaskListTemplateInput input, CancellationToken cancellationToken);
|
||||
Task<bool> UpdateAsync(int id, TaskListTemplateInput input, CancellationToken cancellationToken);
|
||||
Task<bool> DeleteAsync(int id, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -16,4 +16,8 @@
|
|||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="12.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue