This commit is contained in:
npalOmega 2026-03-30 06:49:40 -05:00
parent cd78c93fa8
commit a9fc4b8701
8 changed files with 1829 additions and 67 deletions

View file

@ -0,0 +1,181 @@
using Api.SeaHavenIndustries.DTOs;
using Data.SeaHavenIndustries;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace Api.SeaHavenIndustries.Controllers
{
//[Authorize] // Removed for testing
[ApiController]
[Route("api/Asset")]
public class AssetController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly ApplicationDbContext _db;
public AssetController(UserManager<ApplicationUser> userManager, ApplicationDbContext db)
{
_userManager = userManager;
_db = db;
}
[HttpPost]
[Route("AddAsset")]
public async Task<IActionResult> AddAsset(Asset_DTO asset)
{
try
{
Assets model = new Assets();
model.AssetCode = asset.AssetCode;
model.Name = asset.Name;
model.Description = asset.Description;
model.AccountId = asset.AccountId;
model.LocationId = asset.LocationId;
model.IsActive = asset.IsActive;
_db.Assets.Add(model);
await _db.SaveChangesAsync();
return Ok(new DataResponse { Message = "Asset Created Successfully", Status = "200" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
[HttpPost]
[Route("EditAsset")]
public async Task<IActionResult> EditAsset(EditAsset_DTO asset)
{
try
{
var exist = _db.Assets.Where(w => w.Id == asset.Id).FirstOrDefault();
if (exist == null)
{
return BadRequest(new Response { Status = "Error", Message = "Asset not found!" });
}
exist.AssetCode = asset.AssetCode;
exist.Name = asset.Name;
exist.Description = asset.Description;
exist.AccountId = asset.AccountId;
exist.LocationId = asset.LocationId;
exist.IsActive = asset.IsActive;
_db.Assets.Update(exist);
await _db.SaveChangesAsync();
return Ok(new DataResponse { Message = "Asset Updated Successfully", Status = "200" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
[HttpGet]
[Route("DeleteAsset")]
public async Task<IActionResult> DeleteAsset(int id)
{
try
{
var exist = _db.Assets.Where(w => w.Id == id).FirstOrDefault();
if (exist != null)
{
_db.Remove(exist);
}
else
{
return BadRequest(new Response { Status = "Error", Message = "Asset not found!" });
}
await _db.SaveChangesAsync();
return Ok(new DataResponse { Message = "Asset Deleted Successfully", Status = "200" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
[HttpGet]
[Route("GetAssetById")]
public async Task<IActionResult> GetAssetById(int id)
{
try
{
var asset = await _db.Assets
.Include(a => a.Account)
.Include(a => a.Location)
.Where(w => w.Id == id)
.FirstOrDefaultAsync();
if (asset == null)
{
return NotFound(new Response { Status = "Error", Message = "Asset not found" });
}
return Ok(asset);
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
[HttpGet]
[Route("GetAssetList")]
public IActionResult GetAssetList(string? search = "", int page = 1, int pageSize = 10)
{
// Build the query with search filter
var query = _db.Assets
.Include(a => a.Account)
.Include(a => a.Location)
.Where(s =>
string.IsNullOrEmpty(search) ||
(s.Name != null && s.Name.ToLower().Contains(search.ToLower())) ||
(s.AssetCode != null && s.AssetCode.ToLower().Contains(search.ToLower())) ||
(s.Description != null && s.Description.ToLower().Contains(search.ToLower()))
);
// Get total count AFTER applying search filter
var totalCount = query.Count();
// Get paginated data
var data = query
.Select(n => new
{
n.Id,
n.AssetCode,
n.Name,
n.Description,
n.IsActive,
AccountName = n.Account != null ? n.Account.Name : "",
LocationName = n.Location != null ? n.Location.Name : ""
})
.OrderByDescending(d => d.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToList();
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
var viewModel = new Pagination_DTO
{
Data = data,
PageNumber = page,
PageSize = pageSize,
TotalCount = totalCount,
TotalPages = totalPages
};
return Ok(viewModel);
}
}
}

View file

@ -88,31 +88,67 @@ public class EmployeeController : Controller
// GET: api/Employee/GetEmployeeList
// -----------------------------------
[HttpGet("GetEmployeeList")]
public IActionResult GetEmployeeList(string? search = "")
public IActionResult GetEmployeeList(string? search = "", int page = 1, int pageSize = 12)
{
search ??= "";
var totalCount = _db.Employees.Where(e => !e.IsDeleted.HasValue || e.IsDeleted == false).Count();
var data = _db.Employees
.Include(e => e.JobTitle)
.Include(e => e.Department)
.Include(e => e.Emails)
.Include(e => e.Phones)
.Where(e => !e.IsDeleted.HasValue || e.IsDeleted == false)
.Select(e => new EmployeeListItem_DTO
.Where(e =>
string.IsNullOrWhiteSpace(search) ||
(e.FirstName != null && e.FirstName.ToLower().Contains(search.ToLower())) ||
(e.LastName != null && e.LastName.ToLower().Contains(search.ToLower())) ||
e.Emails.Any(x => x.Address != null && x.Address.ToLower().Contains(search.ToLower())) ||
e.Phones.Any(x => x.Number != null && x.Number.Contains(search))
)
.Select(e => new
{
Id = e.Id,
Name = e.FirstName + " " + e.LastName,
JobTitle = e.JobTitle != null ? e.JobTitle.Name : "",
Role = "",
Email = e.Emails.Where(x => x.IsPrimary == true).Select(x => x.Address).FirstOrDefault(),
Phone = e.Phones.Where(x => x.IsPrimary == true)
.Select(x => x.Number + (x.Extension != null ? " ext. " + x.Extension : ""))
.FirstOrDefault(),
Status = e.Status,
LastLogin = e.LastLogin
e.Id,
e.FirstName,
e.LastName,
JobTitleName = e.JobTitle != null ? e.JobTitle.Name : "",
DepartmentName = e.Department != null ? e.Department.Name : "",
e.Status,
PrimaryEmail = e.Emails.Where(x => x.IsPrimary == true).Select(x => x.Address).FirstOrDefault(),
PrimaryPhone = e.Phones.Where(x => x.IsPrimary == true).Select(x => x.Number).FirstOrDefault(),
PhoneExtension = e.Phones.Where(x => x.IsPrimary == true).Select(x => x.Extension).FirstOrDefault()
})
.ToList();
.OrderByDescending(e => e.Id)
.ToList()
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(e => new
{
id = e.Id,
firstName = e.FirstName,
lastName = e.LastName,
jobTitle = e.JobTitleName ?? "",
email = e.PrimaryEmail ?? "",
phone = e.PrimaryPhone != null
? e.PrimaryPhone + (e.PhoneExtension != null ? " ext. " + e.PhoneExtension : "")
: "",
department = e.DepartmentName ?? "",
status = e.Status
});
return Ok(data);
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
var viewModel = new Pagination_DTO
{
Data = data,
PageNumber = page,
PageSize = pageSize,
TotalCount = totalCount,
TotalPages = totalPages
};
return Ok(viewModel);
}
// -----------------------------------
// GET: api/Employee/DeleteEmployees?id=5

View file

@ -16,7 +16,7 @@ using static System.Runtime.InteropServices.JavaScript.JSType;
namespace Api.SeaHavenIndustries.Controllers
{
[Authorize]
// [Authorize]
[ApiController]
[Route("api/WorkOrder")]
public class WorkOrderController : Controller
@ -58,7 +58,7 @@ namespace Api.SeaHavenIndustries.Controllers
workOrder.SignOffName = model.SignOffName;
workOrder.SignOffSignature = model.SignOffSignature;
workOrder.WorkOrderContacts = workOrder.WorkOrderContacts==null? new List<WorkOrderContacts>(): workOrder.WorkOrderContacts;
workOrder.WorkOrderContacts = workOrder.WorkOrderContacts == null ? new List<WorkOrderContacts>() : workOrder.WorkOrderContacts;
List<int> contactIds = model.GetContactIds();
workOrder.WorkOrderCategories = workOrder.WorkOrderCategories == null ? new List<WorkOrderCategories>() : workOrder.WorkOrderCategories;
List<int> categoryIds = model.GetCategoryIds();
@ -73,7 +73,7 @@ namespace Api.SeaHavenIndustries.Controllers
foreach (int categoryId in categoryIds)
{
// Create association with category
workOrder.WorkOrderCategories.Add(new WorkOrderCategories { CategoryId = categoryId });
workOrder.WorkOrderCategories.Add(new WorkOrderCategories { CategoryId = categoryId });
}
#region Images of workorder
@ -196,13 +196,13 @@ namespace Api.SeaHavenIndustries.Controllers
workOrder.AfterPhotoIssue = model.AfterPhotoIssue;
workOrder.SignOffName = model.SignOffName;
workOrder.SignOffSignature = model.SignOffSignature;
var existcontact=_db.WorkOrderContacts.Where(w=>w.WorkorderId==model.Id).ToList();
if (existcontact.Count()>0)
var existcontact = _db.WorkOrderContacts.Where(w => w.WorkorderId == model.Id).ToList();
if (existcontact.Count() > 0)
{
_db.WorkOrderContacts.RemoveRange(existcontact);
}
var existcategory=_db.workOrderCategories.Where(w=>w.WorkorderId==model.Id).ToList();
if (existcategory.Count()>0)
var existcategory = _db.workOrderCategories.Where(w => w.WorkorderId == model.Id).ToList();
if (existcategory.Count() > 0)
{
_db.workOrderCategories.RemoveRange(existcategory);
}
@ -316,36 +316,64 @@ namespace Api.SeaHavenIndustries.Controllers
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
//[HttpGet]
//[Route("GetWorkorder")]
//public IActionResult GetWorkorder(int page = 1, int pageSize = 10, string search="")
//{
// var totalCount = _db.workOrders.Count();
// var data = _db.workOrders.Include(w => w.Locations).AsNoTracking().Where(w => w.istemplate != true && (w.WorkerOrderTitle.ToLower().Contains(search.ToLower()) || w.WorkerOrderNumber.ToLower().Contains(search.ToLower()) || w.Locations.Title.ToLower().Contains(search.ToLower()))).Select(s => new
// {
// s.Id,
// s.WorkerOrderTitle,
// s.DueDate,
// s.Priority,
// s.Status,
// site = s.Locations.Name
// }).OrderByDescending(d=> d.Id).ToList().Skip((page - 1) * pageSize)
// .Take(pageSize);
// -----------------------------------
// GET: api/WorkOrder/GetWorkOrderList
// -----------------------------------
[HttpGet("GetWorkOrderList")]
public IActionResult GetWorkOrderList(string? search = "", int page = 1, int pageSize = 12)
{
search ??= "";
// // Calculate the number of pages
// var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
var query = _db.workOrders
.Include(w => w.Locations)
.Include(w => w.AssignToUser)
.Where(w => w.istemplate != true);
// // Create a view model containing the paged items and pagination metadata
// var viewModel = new Pagination_DTO
// {
// Data = data,
// PageNumber = page,
// PageSize = pageSize,
// TotalCount = totalCount,
// TotalPages = totalPages
// };
// return Ok(viewModel);
//}
if (!string.IsNullOrWhiteSpace(search))
{
var s = search.ToLower();
query = query.Where(w =>
(w.WorkerOrderNumber ?? "").ToLower().Contains(s) ||
(w.WorkerOrderTitle ?? "").ToLower().Contains(s) ||
(w.Locations.Title ?? "").ToLower().Contains(s) ||
(w.Locations.Name ?? "").ToLower().Contains(s)
);
}
var totalCount = query.Count();
var data = query
.OrderByDescending(w => w.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(w => new
{
id = w.Id,
workOrderNumber = w.WorkerOrderNumber,
number = w.WorkerOrderNumber,
title = w.WorkerOrderTitle,
location = w.Locations != null ? w.Locations.Name : "",
locationName = w.Locations != null ? w.Locations.Name : "",
priority = w.Priority,
status = w.Status,
createdDate = w.CreatedDate,
dueDate = w.DueDate,
assignedTo = w.AssignToUser != null ? w.AssignToUser.FirstName + " " + w.AssignToUser.LastName : "",
employeeName = w.AssignToUser != null ? w.AssignToUser.FirstName + " " + w.AssignToUser.LastName : ""
})
.ToList();
var viewModel = new Pagination_DTO
{
Data = data,
PageNumber = page,
PageSize = pageSize,
TotalCount = totalCount,
TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize)
};
return Ok(viewModel);
}
[HttpDelete]
@ -353,8 +381,8 @@ namespace Api.SeaHavenIndustries.Controllers
public IActionResult Deleteworkorder([FromBody] EditWorkorder_DTO model)
{
int id = model.Id.HasValue ? model.Id.Value : 0;
if (id == 0)
{
if (id == 0)
{
return BadRequest("Id is required");
}
try
@ -435,14 +463,14 @@ namespace Api.SeaHavenIndustries.Controllers
if (assignto != null && assignto.Length > 0)
{
if (assignto[0] != "" && assignto[0]!=null)
query = query.Where(w => assignto.Contains(w.AssignTo));
if (assignto[0] != "" && assignto[0] != null)
query = query.Where(w => assignto.Contains(w.AssignTo));
}
if (location != null && location.Length > 0)
{
if (location[0]!=0)
query = query.Where(w => location.Contains(w.LocationId ?? 0));
if (location[0] != 0)
query = query.Where(w => location.Contains(w.LocationId ?? 0));
}
if (priority != null && priority.Length > 0)
@ -520,7 +548,7 @@ namespace Api.SeaHavenIndustries.Controllers
{
s.Id,
s.WorkerOrderNumber,
title= s.WorkerOrderTitle,
title = s.WorkerOrderTitle,
s.DueDate,
s.Status,
assignee = s.AssignToUser.FirstName,
@ -528,9 +556,9 @@ namespace Api.SeaHavenIndustries.Controllers
priority = s.Priority,
s.PO,
s.TT,
poc = s.WorkOrderContacts.Select(s => new {s.Id,name=(s.POC.FirstName + " " + s.POC.LastName).Trim(),email=s.POC.Email,phone=s.POC.PhoneNumber}),
category = s.WorkOrderCategories.Select(s => new {s.Id,name=s.Category.Name}),
Attachments = s.workOrderAttachments.Select(s => new {s.Id,attachment=s.Attachments}),
poc = s.WorkOrderContacts.Select(s => new { s.Id, name = (s.POC.FirstName + " " + s.POC.LastName).Trim(), email = s.POC.Email, phone = s.POC.PhoneNumber }),
category = s.WorkOrderCategories.Select(s => new { s.Id, name = s.Category.Name }),
Attachments = s.workOrderAttachments.Select(s => new { s.Id, attachment = s.Attachments }),
//category = s.Category.Name,
//s.Attachments,
s.BeforPhotoIssue,
@ -540,7 +568,7 @@ namespace Api.SeaHavenIndustries.Controllers
s.SignOffName,
s.SignOffAttachment,
s.SignOffSignature,
Comments = s.Comments.Select(s => new { s.Id, s.CreatedDate, s.Commenttext, s.Documents,userName=s.ApplicationUser.FirstName }),
Comments = s.Comments.Select(s => new { s.Id, s.CreatedDate, s.Commenttext, s.Documents, userName = s.ApplicationUser.FirstName }),
quotes = s.Quotes.Select(q => new { code = q.QuotId, q.Id })
}).FirstOrDefaultAsync();
if (exist == null)
@ -555,7 +583,7 @@ namespace Api.SeaHavenIndustries.Controllers
}
[HttpGet]
[Route("GetFilteredWorkorder2")]
public IActionResult GetFilteredWorkorder2([FromQuery] string[] assignto, [FromQuery] int[]? location, [FromQuery] string[]? priority, [FromQuery] string[]? status, [FromQuery] string[]? duedate, [FromQuery] string search = "", [FromQuery] string sort = "Sort Order", [FromQuery] string sortby= "Sort By", int page = 1, int pageSize = 10)
public IActionResult GetFilteredWorkorder2([FromQuery] string[] assignto, [FromQuery] int[]? location, [FromQuery] string[]? priority, [FromQuery] string[]? status, [FromQuery] string[]? duedate, [FromQuery] string search = "", [FromQuery] string sort = "Sort Order", [FromQuery] string sortby = "Sort By", int page = 1, int pageSize = 10)
{
try
{
@ -596,9 +624,9 @@ namespace Api.SeaHavenIndustries.Controllers
.ToList();
// Perform client-side filtering for due dates
data = data.Where(w =>
duedate.Count() == 0 ||
duedate.Any(dd => w.DueDate.HasValue && CheckDueDate(dd, w.DueDate.Value.Date, currentDate))).ToList();
data = data.Where(w =>
duedate.Count() == 0 ||
duedate.Any(dd => w.DueDate.HasValue && CheckDueDate(dd, w.DueDate.Value.Date, currentDate))).ToList();
// Apply sorting
if (!string.IsNullOrEmpty(sort) && !string.IsNullOrEmpty(sortby) && sortby != "")
@ -619,7 +647,7 @@ namespace Api.SeaHavenIndustries.Controllers
break;
}
}
var newdata=data.Select(s => new { s.Id, s.WorkerOrderTitle, s.DueDate, s.Status, s.Priority, s.Locations.Name }).OrderByDescending(d => d.Name).ToList().Skip((page - 1) * pageSize)
var newdata = data.Select(s => new { s.Id, s.WorkerOrderTitle, s.DueDate, s.Status, s.Priority, s.Locations.Name }).OrderByDescending(d => d.Name).ToList().Skip((page - 1) * pageSize)
.Take(pageSize);
var totalPages1 = (int)Math.Ceiling(totalCount / (double)pageSize);
@ -637,7 +665,7 @@ namespace Api.SeaHavenIndustries.Controllers
catch (Exception ex)
{
var totalCount = _db.workOrders.Where(w => w.istemplate != true).Count();
var data1 = _db.workOrders.Where(w => w.istemplate != true).Include(w => w.Locations).Select(s=> new
var data1 = _db.workOrders.Where(w => w.istemplate != true).Include(w => w.Locations).Select(s => new
{
s.Id,
s.WorkerOrderTitle,
@ -810,7 +838,7 @@ namespace Api.SeaHavenIndustries.Controllers
public DateTime? DueDate { get; set; }
public string FormattedDueDate
{
get { return DueDate.HasValue? DueDate.Value.ToString("MM/dd/yyyy"): ""; }
get { return DueDate.HasValue ? DueDate.Value.ToString("MM/dd/yyyy") : ""; }
}
public string? Dispatcher { get; set; }
public string? Scheduler { get; set; }

View file

@ -0,0 +1,13 @@
namespace Api.SeaHavenIndustries.DTOs
{
public class Asset_DTO
{
public string AssetCode { get; set; } = null!;
public string Name { get; set; } = null!;
public string? Description { get; set; }
public int? AccountId { get; set; }
public int? LocationId { get; set; }
public bool IsActive { get; set; } = true;
}
}

View file

@ -0,0 +1,14 @@
namespace Api.SeaHavenIndustries.DTOs
{
public class EditAsset_DTO
{
public int Id { get; set; }
public string AssetCode { get; set; } = null!;
public string Name { get; set; } = null!;
public string? Description { get; set; }
public int? AccountId { get; set; }
public int? LocationId { get; set; }
public bool IsActive { get; set; } = true;
}
}

View file

@ -0,0 +1,929 @@
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[Contacts] Script Date: 1/21/2026 4:16:02 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Contacts](
[Id] [int] IDENTITY(1,1) NOT NULL,
[PhoneNumber] [nvarchar](max) NULL,
[Email] [nvarchar](max) NULL,
[IsDeleted] [bit] NULL,
[createdby] [nvarchar](max) NULL,
[DeleterUserId] [nvarchar](max) NULL,
[DeletionTime] [datetime2](7) NULL,
[CreatedDate] [datetime2](7) NULL,
[LastModificationTime] [datetime2](7) NULL,
[LastModifierUserId] [int] NULL,
[Title] [nvarchar](200) NULL,
[Owner] [nvarchar](200) NULL,
[FirstName] [nvarchar](100) NULL,
[MiddleName] [nvarchar](100) NULL,
[LastName] [nvarchar](100) NULL,
[ContactType] [nvarchar](100) NULL,
[Address1] [nvarchar](255) NULL,
[Address2] [nvarchar](255) NULL,
[City] [nvarchar](100) NULL,
[State] [nvarchar](100) NULL,
[Zip] [nvarchar](20) NULL,
[FacebookUrl] [nvarchar](255) NULL,
[LinkedInUrl] [nvarchar](255) NULL,
[TwitterUrl] [nvarchar](255) NULL,
[LocationId] [int] NULL,
[AccountId] [int] NULL,
CONSTRAINT [PK_Contacts] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[Contacts] WITH CHECK ADD CONSTRAINT [FK_Contacts_Accounts] FOREIGN KEY([AccountId])
REFERENCES [dbo].[Accounts] ([Id])
ON DELETE SET NULL
GO
ALTER TABLE [dbo].[Contacts] CHECK CONSTRAINT [FK_Contacts_Accounts]
GO
ALTER TABLE [dbo].[Contacts] WITH CHECK ADD CONSTRAINT [FK_Contacts_Locations] FOREIGN KEY([LocationId])
REFERENCES [dbo].[Locations] ([Id])
ON DELETE SET NULL
GO
ALTER TABLE [dbo].[Contacts] CHECK CONSTRAINT [FK_Contacts_Locations]
GO
CREATE TABLE Assets (
Id INT IDENTITY(1,1) PRIMARY KEY,
AssetCode NVARCHAR(50) NOT NULL,
Name NVARCHAR(255) NOT NULL,
Description NVARCHAR(MAX) NULL,
AccountId INT NULL,
LocationId INT NULL,
IsActive BIT NOT NULL DEFAULT 1,
CreatedDate DATETIME2 NOT NULL DEFAULT GETUTCDATE(),
CreatedBy INT NULL,
LastModificationTime DATETIME2 NULL,
LastModifiedBy INT NULL,
CONSTRAINT FK_Assets_Accounts
FOREIGN KEY (AccountId) REFERENCES Accounts(Id),
CONSTRAINT FK_Assets_Locations
FOREIGN KEY (LocationId) REFERENCES Locations(Id)
);
CREATE TABLE PMSchedules (
Id INT IDENTITY(1,1) PRIMARY KEY,
Name NVARCHAR(255) NOT NULL,
Frequency NVARCHAR(50) NOT NULL, -- Monthly / Quarterly / Yearly
StartDate DATE NOT NULL,
IsActive BIT NOT NULL DEFAULT 1,
AssetId INT NULL,
LocationId INT NULL,
CreatedDate DATETIME2 NOT NULL DEFAULT GETUTCDATE(),
CreatedBy INT NULL,
LastModificationTime DATETIME2 NULL,
LastModifiedBy INT NULL,
CONSTRAINT FK_PMSchedules_Assets
FOREIGN KEY (AssetId) REFERENCES Assets(Id),
CONSTRAINT FK_PMSchedules_Locations
FOREIGN KEY (LocationId) REFERENCES Locations(Id)
);
CREATE TABLE FollowUps (
Id INT IDENTITY(1,1) PRIMARY KEY,
Reason NVARCHAR(MAX) NOT NULL,
ScheduleDate DATETIME2 NOT NULL,
ScheduleStartTime TIME NOT NULL,
Status NVARCHAR(20) NOT NULL DEFAULT 'Open', -- Open | Completed | Cancelled
EmployeeId INT NULL,
WorkOrderId INT NULL,
AccountId INT NULL,
LocationId INT NULL,
CreatedDate DATETIME2 NOT NULL DEFAULT GETUTCDATE(),
CreatedBy INT NULL,
LastModificationTime DATETIME2 NULL,
LastModifiedBy INT NULL,
CONSTRAINT FK_FollowUps_Employees
FOREIGN KEY (EmployeeId) REFERENCES Employees(Id),
CONSTRAINT FK_FollowUps_WorkOrders
FOREIGN KEY (WorkOrderId) REFERENCES WorkOrders(Id),
CONSTRAINT FK_FollowUps_Accounts
FOREIGN KEY (AccountId) REFERENCES Accounts(Id),
CONSTRAINT FK_FollowUps_Locations
FOREIGN KEY (LocationId) REFERENCES Locations(Id)
);
CREATE TABLE Employees (
Id INT IDENTITY(1,1) PRIMARY KEY,
-- General Details
FirstName NVARCHAR(50) NOT NULL,
MiddleName NVARCHAR(50) NULL,
LastName NVARCHAR(50) NOT NULL,
DepartmentId INT NULL,
TimeZone NVARCHAR(200) NULL,
Gender INT NULL, -- 0=Male,1=Female,2=Not specified,3=Other
DateOfBirth DATE NULL,
JobTitleId INT NULL,
EmployeeNumber NVARCHAR(50) NULL,
HireDate DATE NULL,
RegionId INT NULL,
CanChangeProviderNumber BIT NULL,
About NVARCHAR(255) NULL,
-- Login & Role (stored locally, no Identity)
PermissionGroupId INT NULL, -- LoginInfo.PermissionGroupID
UserName NVARCHAR(256) NULL, -- LoginInfo.UserName
ConfirmUserName NVARCHAR(256) NULL, -- LoginInfo.ConfirmUserName
-- Additional Details
SMSAddress NVARCHAR(270) NULL,
Website NVARCHAR(2048) NULL,
-- Social Media
FacebookUrl NVARCHAR(2048) NULL,
LinkedInUrl NVARCHAR(2048) NULL,
TwitterUrl NVARCHAR(2048) NULL,
-- Status (for list grid)
Status NVARCHAR(50) NULL, -- "Active", "Offline", "Awaiting Confirmation"
LastLogin DATETIME NULL,
-- Soft delete / audit
IsDeleted BIT NULL,
CreatedBy NVARCHAR(MAX) NULL,
CreatedDate DATETIME2 NULL,
LastModificationTime DATETIME2 NULL,
LastModifierUserId INT NULL
);
CREATE TABLE EmployeeAddresses (
Id INT IDENTITY(1,1) PRIMARY KEY,
EmployeeId INT NOT NULL,
Street NVARCHAR(250) NULL,
StreetLineTwo NVARCHAR(250) NULL,
City NVARCHAR(100) NULL,
Zip NVARCHAR(20) NULL,
CountryCode NVARCHAR(10) NULL, -- "US"
StateCode NVARCHAR(10) NULL, -- "NY", "CA", etc.
CountyId INT NULL,
FOREIGN KEY (EmployeeId) REFERENCES Employees(Id) ON DELETE CASCADE
);
CREATE TABLE EmployeePhones (
Id INT IDENTITY(1,1) PRIMARY KEY,
EmployeeId INT NOT NULL,
PhoneTypeId INT NULL, -- 1=Work, 2=Home, etc.
Number NVARCHAR(25) NULL,
Extension NVARCHAR(10) NULL,
IsPrimary BIT NULL,
FOREIGN KEY (EmployeeId) REFERENCES Employees(Id) ON DELETE CASCADE
);
CREATE TABLE EmployeeEmails (
Id INT IDENTITY(1,1) PRIMARY KEY,
EmployeeId INT NOT NULL,
EmailTypeId INT NULL, -- 1=Work,2=Home,3=Other
Address NVARCHAR(256) NULL,
IsPrimary BIT NULL,
FOREIGN KEY (EmployeeId) REFERENCES Employees(Id) ON DELETE CASCADE
);
CREATE TABLE Departments (
Id INT IDENTITY(1,1) PRIMARY KEY,
Name NVARCHAR(200) NOT NULL
);
CREATE TABLE JobTitles (
Id INT IDENTITY(1,1) PRIMARY KEY,
Name NVARCHAR(200) NOT NULL
);
CREATE TABLE Regions (
Id INT IDENTITY(1,1) PRIMARY KEY,
Name NVARCHAR(200) NOT NULL
);
CREATE TABLE PermissionGroups (
Id INT IDENTITY(1,1) PRIMARY KEY,
Name NVARCHAR(200) NOT NULL
);
CREATE TABLE PhoneTypes (
Id INT IDENTITY(1,1) PRIMARY KEY,
Name NVARCHAR(50) NOT NULL
);
CREATE TABLE EmailTypes (
Id INT IDENTITY(1,1) PRIMARY KEY,
Name NVARCHAR(50) NOT NULL
);
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[Accounts] Script Date: 1/21/2026 4:10:39 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Accounts](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](255) NULL,
[Type] [nvarchar](100) NULL,
[Importance] [int] NULL,
[TerritoryId] [int] NULL,
[OwnerEmployeeId] [int] NULL,
[PrivacySetting] [nvarchar](100) NULL,
[About] [nvarchar](max) NULL,
[Tags] [nvarchar](500) NULL,
[Phone] [nvarchar](50) NULL,
[Email] [nvarchar](150) NULL,
[IndustryId] [int] NULL,
[TimeZone] [nvarchar](100) NULL,
[Website] [nvarchar](255) NULL,
[CurrencyCode] [nvarchar](50) NULL,
[ServiceInstructions] [nvarchar](max) NULL,
[FacebookUrl] [nvarchar](255) NULL,
[LinkedInUrl] [nvarchar](255) NULL,
[TwitterUrl] [nvarchar](255) NULL,
[IsDeleted] [bit] NULL,
[createdby] [nvarchar](450) NULL,
[DeleterUserId] [nvarchar](450) NULL,
[DeletionTime] [datetime] NULL,
[CreatedDate] [datetime] NULL,
[LastModificationTime] [datetime] NULL,
[LastModifierUserId] [int] NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[Accounts] ADD DEFAULT ((0)) FOR [IsDeleted]
GO
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[workOrderAttachments] Script Date: 1/21/2026 4:30:43 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[workOrderAttachments](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Attachments] [nvarchar](max) NULL,
[WorkorderId] [int] NULL,
[IsDeleted] [bit] NULL,
[createdby] [nvarchar](max) NULL,
[DeleterUserId] [nvarchar](max) NULL,
[DeletionTime] [datetime2](7) NULL,
[CreatedDate] [datetime2](7) NULL,
[LastModificationTime] [datetime2](7) NULL,
[LastModifierUserId] [int] NULL,
CONSTRAINT [PK_workOrderAttachments] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[workOrderAttachments] WITH CHECK ADD CONSTRAINT [FK_workOrderAttachments_workOrders_WorkorderId] FOREIGN KEY([WorkorderId])
REFERENCES [dbo].[workOrders] ([Id])
GO
ALTER TABLE [dbo].[workOrderAttachments] CHECK CONSTRAINT [FK_workOrderAttachments_workOrders_WorkorderId]
GO
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[workOrderCategories] Script Date: 1/21/2026 4:31:16 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[workOrderCategories](
[Id] [int] IDENTITY(1,1) NOT NULL,
[CategoryId] [int] NULL,
[WorkorderId] [int] NULL,
[IsDeleted] [bit] NULL,
[createdby] [nvarchar](max) NULL,
[DeleterUserId] [nvarchar](max) NULL,
[DeletionTime] [datetime2](7) NULL,
[CreatedDate] [datetime2](7) NULL,
[LastModificationTime] [datetime2](7) NULL,
[LastModifierUserId] [int] NULL,
CONSTRAINT [PK_workOrderCategories] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[workOrderCategories] WITH CHECK ADD CONSTRAINT [FK_workOrderCategories_Categories_CategoryId] FOREIGN KEY([CategoryId])
REFERENCES [dbo].[Categories] ([Id])
GO
ALTER TABLE [dbo].[workOrderCategories] CHECK CONSTRAINT [FK_workOrderCategories_Categories_CategoryId]
GO
ALTER TABLE [dbo].[workOrderCategories] WITH CHECK ADD CONSTRAINT [FK_workOrderCategories_workOrders_WorkorderId] FOREIGN KEY([WorkorderId])
REFERENCES [dbo].[workOrders] ([Id])
GO
ALTER TABLE [dbo].[workOrderCategories] CHECK CONSTRAINT [FK_workOrderCategories_workOrders_WorkorderId]
GO
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[WorkOrderContacts] Script Date: 1/21/2026 4:31:56 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[WorkOrderContacts](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ContactId] [int] NULL,
[WorkorderId] [int] NULL,
[IsDeleted] [bit] NULL,
[createdby] [nvarchar](max) NULL,
[DeleterUserId] [nvarchar](max) NULL,
[DeletionTime] [datetime2](7) NULL,
[CreatedDate] [datetime2](7) NULL,
[LastModificationTime] [datetime2](7) NULL,
[LastModifierUserId] [int] NULL,
CONSTRAINT [PK_WorkOrderContacts] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[WorkOrderContacts] WITH CHECK ADD CONSTRAINT [FK_WorkOrderContacts_Contacts_ContactId] FOREIGN KEY([ContactId])
REFERENCES [dbo].[Contacts] ([Id])
GO
ALTER TABLE [dbo].[WorkOrderContacts] CHECK CONSTRAINT [FK_WorkOrderContacts_Contacts_ContactId]
GO
ALTER TABLE [dbo].[WorkOrderContacts] WITH CHECK ADD CONSTRAINT [FK_WorkOrderContacts_workOrders_WorkorderId] FOREIGN KEY([WorkorderId])
REFERENCES [dbo].[workOrders] ([Id])
GO
ALTER TABLE [dbo].[WorkOrderContacts] CHECK CONSTRAINT [FK_WorkOrderContacts_workOrders_WorkorderId]
GO
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[workOrders] Script Date: 1/21/2026 4:32:35 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[workOrders](
[Id] [int] IDENTITY(1,1) NOT NULL,
[WorkerOrderNumber] [nvarchar](max) NULL,
[WorkerOrderTitle] [nvarchar](max) NULL,
[Description] [nvarchar](max) NULL,
[PO] [nvarchar](max) NULL,
[TT] [nvarchar](max) NULL,
[SignOffAttachment] [nvarchar](max) NULL,
[AssignTo] [nvarchar](450) NULL,
[DueDate] [datetime2](7) NULL,
[Priority] [nvarchar](max) NULL,
[Status] [nvarchar](max) NULL,
[LocationId] [int] NULL,
[Attachments] [nvarchar](max) NULL,
[BeforPhotoIssue] [nvarchar](max) NULL,
[BeforPhotoAttachment] [nvarchar](max) NULL,
[AfterPhotoIssue] [nvarchar](max) NULL,
[AfterPhotoAttachment] [nvarchar](max) NULL,
[SignOffName] [nvarchar](max) NULL,
[SignOffSignature] [nvarchar](max) NULL,
[IsDeleted] [bit] NULL,
[createdby] [nvarchar](max) NULL,
[DeleterUserId] [nvarchar](max) NULL,
[DeletionTime] [datetime2](7) NULL,
[CreatedDate] [datetime2](7) NULL,
[LastModificationTime] [datetime2](7) NULL,
[LastModifierUserId] [int] NULL,
[istemplate] [bit] NULL,
[WorkOrderType] [varchar](50) NULL,
[AvettaTask] [varchar](max) NULL,
[AssignDate] [date] NULL,
CONSTRAINT [PK_workOrders] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[workOrders] WITH CHECK ADD CONSTRAINT [FK_workOrders_AspNetUsers_AssignTo] FOREIGN KEY([AssignTo])
REFERENCES [dbo].[AspNetUsers] ([Id])
GO
ALTER TABLE [dbo].[workOrders] CHECK CONSTRAINT [FK_workOrders_AspNetUsers_AssignTo]
GO
ALTER TABLE [dbo].[workOrders] WITH CHECK ADD CONSTRAINT [FK_workOrders_Locations_LocationId] FOREIGN KEY([LocationId])
REFERENCES [dbo].[Locations] ([Id])
GO
ALTER TABLE [dbo].[workOrders] CHECK CONSTRAINT [FK_workOrders_Locations_LocationId]
GO
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[Templates] Script Date: 1/21/2026 4:33:35 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Templates](
[Id] [int] IDENTITY(1,1) NOT NULL,
[WorkerOrderNumber] [nvarchar](max) NULL,
[WorkerOrderTitle] [nvarchar](max) NULL,
[Description] [nvarchar](max) NULL,
[PO] [nvarchar](max) NULL,
[TT] [nvarchar](max) NULL,
[SignOffAttachment] [nvarchar](max) NULL,
[AssignTo] [nvarchar](450) NULL,
[DueDate] [datetime2](7) NULL,
[Priority] [nvarchar](max) NULL,
[Status] [nvarchar](max) NULL,
[LocationId] [int] NULL,
[CategoryId] [int] NULL,
[Attachments] [nvarchar](max) NULL,
[BeforPhotoIssue] [nvarchar](max) NULL,
[BeforPhotoAttachment] [nvarchar](max) NULL,
[AfterPhotoIssue] [nvarchar](max) NULL,
[AfterPhotoAttachment] [nvarchar](max) NULL,
[SignOffName] [nvarchar](max) NULL,
[SignOffSignature] [nvarchar](max) NULL,
[IsDeleted] [bit] NULL,
[createdby] [nvarchar](max) NULL,
[DeleterUserId] [nvarchar](max) NULL,
[DeletionTime] [datetime2](7) NULL,
[CreatedDate] [datetime2](7) NULL,
[LastModificationTime] [datetime2](7) NULL,
[LastModifierUserId] [int] NULL,
[ContactId] [int] NULL,
CONSTRAINT [PK_Templates] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[Templates] WITH CHECK ADD CONSTRAINT [FK_Templates_AspNetUsers_AssignTo] FOREIGN KEY([AssignTo])
REFERENCES [dbo].[AspNetUsers] ([Id])
GO
ALTER TABLE [dbo].[Templates] CHECK CONSTRAINT [FK_Templates_AspNetUsers_AssignTo]
GO
ALTER TABLE [dbo].[Templates] WITH CHECK ADD CONSTRAINT [FK_Templates_Categories_CategoryId] FOREIGN KEY([CategoryId])
REFERENCES [dbo].[Categories] ([Id])
GO
ALTER TABLE [dbo].[Templates] CHECK CONSTRAINT [FK_Templates_Categories_CategoryId]
GO
ALTER TABLE [dbo].[Templates] WITH CHECK ADD CONSTRAINT [FK_Templates_Contacts_ContactId] FOREIGN KEY([ContactId])
REFERENCES [dbo].[Contacts] ([Id])
GO
ALTER TABLE [dbo].[Templates] CHECK CONSTRAINT [FK_Templates_Contacts_ContactId]
GO
ALTER TABLE [dbo].[Templates] WITH CHECK ADD CONSTRAINT [FK_Templates_Locations_LocationId] FOREIGN KEY([LocationId])
REFERENCES [dbo].[Locations] ([Id])
GO
ALTER TABLE [dbo].[Templates] CHECK CONSTRAINT [FK_Templates_Locations_LocationId]
GO
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[Regions] Script Date: 1/21/2026 4:34:46 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Regions](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](200) NOT NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[QuotesLines] Script Date: 1/21/2026 4:35:20 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[QuotesLines](
[Id] [int] IDENTITY(1,1) NOT NULL,
[LineitemNumber] [int] NULL,
[Lineitem] [decimal](18, 2) NULL,
[QuoteId] [int] NULL,
[IsDeleted] [bit] NULL,
[createdby] [nvarchar](max) NULL,
[DeleterUserId] [nvarchar](max) NULL,
[DeletionTime] [datetime2](7) NULL,
[CreatedDate] [datetime2](7) NULL,
[LastModificationTime] [datetime2](7) NULL,
[LastModifierUserId] [int] NULL,
[Descriptions] [nvarchar](max) NULL,
CONSTRAINT [PK_QuotesLines] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[QuotesLines] WITH CHECK ADD CONSTRAINT [FK_QuotesLines_Quotes_QuoteId] FOREIGN KEY([QuoteId])
REFERENCES [dbo].[Quotes] ([Id])
GO
ALTER TABLE [dbo].[QuotesLines] CHECK CONSTRAINT [FK_QuotesLines_Quotes_QuoteId]
GO
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[Quotes] Script Date: 1/21/2026 4:35:57 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Quotes](
[Id] [int] IDENTITY(1,1) NOT NULL,
[QuotId] [nvarchar](max) NULL,
[Date] [datetime2](7) NULL,
[BillingAddress] [nvarchar](max) NULL,
[TT] [nvarchar](max) NULL,
[PO] [nvarchar](max) NULL,
[ContactId] [int] NULL,
[WorkorderId] [int] NULL,
[ProjectName] [nvarchar](max) NULL,
[SiteName] [nvarchar](max) NULL,
[SiteId] [nvarchar](max) NULL,
[ShippingAddress] [nvarchar](max) NULL,
[IsDeleted] [bit] NULL,
[createdby] [nvarchar](max) NULL,
[DeleterUserId] [nvarchar](max) NULL,
[DeletionTime] [datetime2](7) NULL,
[CreatedDate] [datetime2](7) NULL,
[LastModificationTime] [datetime2](7) NULL,
[LastModifierUserId] [int] NULL,
[Status] [nvarchar](max) NULL,
CONSTRAINT [PK_Quotes] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[Quotes] WITH CHECK ADD CONSTRAINT [FK_Quotes_Contacts_ContactId] FOREIGN KEY([ContactId])
REFERENCES [dbo].[Contacts] ([Id])
GO
ALTER TABLE [dbo].[Quotes] CHECK CONSTRAINT [FK_Quotes_Contacts_ContactId]
GO
ALTER TABLE [dbo].[Quotes] WITH CHECK ADD CONSTRAINT [FK_Quotes_workOrders_WorkorderId] FOREIGN KEY([WorkorderId])
REFERENCES [dbo].[workOrders] ([Id])
GO
ALTER TABLE [dbo].[Quotes] CHECK CONSTRAINT [FK_Quotes_workOrders_WorkorderId]
GO
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[PermissionGroups] Script Date: 1/21/2026 4:36:37 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[PermissionGroups](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](200) NOT NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[Locations] Script Date: 1/21/2026 4:37:09 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Locations](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Title] [nvarchar](max) NULL,
[Name] [nvarchar](max) NULL,
[Latitude] [nvarchar](max) NULL,
[Longitude] [nvarchar](max) NULL,
[IsDeleted] [bit] NULL,
[createdby] [nvarchar](max) NULL,
[DeleterUserId] [nvarchar](max) NULL,
[DeletionTime] [datetime2](7) NULL,
[CreatedDate] [datetime2](7) NULL,
[LastModificationTime] [datetime2](7) NULL,
[LastModifierUserId] [int] NULL,
CONSTRAINT [PK_Locations] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[JobTitles] Script Date: 1/21/2026 4:39:15 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[JobTitles](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](200) NOT NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[ForgetPasswordCodes] Script Date: 1/21/2026 4:41:21 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[ForgetPasswordCodes](
[Id] [int] IDENTITY(1,1) NOT NULL,
[UserId] [nvarchar](max) NOT NULL,
[Email] [nvarchar](max) NOT NULL,
[Code] [nvarchar](max) NOT NULL,
CONSTRAINT [PK_ForgetPasswordCodes] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[Categories] Script Date: 1/21/2026 4:42:50 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Categories](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NULL,
[IsDeleted] [bit] NULL,
[createdby] [nvarchar](max) NULL,
[DeleterUserId] [nvarchar](max) NULL,
[DeletionTime] [datetime2](7) NULL,
[CreatedDate] [datetime2](7) NULL,
[LastModificationTime] [datetime2](7) NULL,
[LastModifierUserId] [int] NULL,
CONSTRAINT [PK_Categories] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[Comments] Script Date: 1/21/2026 4:44:45 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Comments](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Commenttext] [nvarchar](max) NULL,
[WorkerOrderId] [int] NULL,
[IsDeleted] [bit] NULL,
[createdby] [nvarchar](max) NULL,
[DeleterUserId] [nvarchar](max) NULL,
[DeletionTime] [datetime2](7) NULL,
[CreatedDate] [datetime2](7) NULL,
[LastModificationTime] [datetime2](7) NULL,
[LastModifierUserId] [int] NULL,
[UserId] [nvarchar](450) NULL,
[Documents] [nvarchar](max) NULL,
CONSTRAINT [PK_Comments] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[Comments] WITH CHECK ADD CONSTRAINT [FK_Comments_AspNetUsers_UserId] FOREIGN KEY([UserId])
REFERENCES [dbo].[AspNetUsers] ([Id])
GO
ALTER TABLE [dbo].[Comments] CHECK CONSTRAINT [FK_Comments_AspNetUsers_UserId]
GO
ALTER TABLE [dbo].[Comments] WITH CHECK ADD CONSTRAINT [FK_Comments_workOrders_WorkerOrderId] FOREIGN KEY([WorkerOrderId])
REFERENCES [dbo].[workOrders] ([Id])
GO
ALTER TABLE [dbo].[Comments] CHECK CONSTRAINT [FK_Comments_workOrders_WorkerOrderId]
GO
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[ContactDetails] Script Date: 1/21/2026 4:45:14 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[ContactDetails](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ContactId] [int] NOT NULL,
[Gender] [nvarchar](50) NULL,
[DateOfBirth] [datetime] NULL,
[Territory] [nvarchar](100) NULL,
[PrivacySetting] [nvarchar](100) NULL,
[About] [nvarchar](max) NULL,
[Tags] [nvarchar](500) NULL,
[FacebookUrl] [nvarchar](255) NULL,
[LinkedInUrl] [nvarchar](255) NULL,
[TwitterUrl] [nvarchar](255) NULL,
[IsDeleted] [bit] NULL,
[createdby] [nvarchar](450) NULL,
[DeleterUserId] [nvarchar](450) NULL,
[DeletionTime] [datetime] NULL,
[CreatedDate] [datetime] NULL,
[LastModificationTime] [datetime] NULL,
[LastModifierUserId] [int] NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[ContactDetails] ADD DEFAULT ((0)) FOR [IsDeleted]
GO
ALTER TABLE [dbo].[ContactDetails] WITH CHECK ADD CONSTRAINT [FK_ContactDetails_Contacts] FOREIGN KEY([ContactId])
REFERENCES [dbo].[Contacts] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[ContactDetails] CHECK CONSTRAINT [FK_ContactDetails_Contacts]
GO
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[Departments] Script Date: 1/21/2026 4:45:43 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Departments](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](200) NOT NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
USE [db_a7141e_seahavenindustry]
GO
/****** Object: Table [dbo].[EmailTypes] Script Date: 1/21/2026 4:46:25 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[EmailTypes](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NOT NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

553
CALENDAR_MODULE_COMPLETE.cs Normal file
View file

@ -0,0 +1,553 @@
/*
===================================================================================
CALENDAR/EVENTS MODULE - COMPLETE IMPLEMENTATION
===================================================================================
This file contains everything needed for the Calendar/Events module:
1. Events Model (Database Entity)
2. Event_DTO (for GET responses)
3. EditEvent_DTO (for POST/PUT requests)
4. CalendarController (API endpoints)
5. SQL Script for database table creation and sample data
Author: AI Agent
Date: 2026-03-30
===================================================================================
*/
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
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;
// ===================================================================================
// SECTION 1: DATABASE MODEL (Data.SeaHavenIndustries/Models/Events.cs)
// ===================================================================================
namespace Data.SeaHavenIndustries
{
public class Events : FullAuditEntity
{
public string Title { get; set; } = null!;
public string? Description { get; set; }
public string? Location { get; set; }
// Related Record (can link to Accounts, Contacts, Work Orders, etc.)
public int? RecordID { get; set; }
public string? RecordName { get; set; }
public string? RecordTargetType { get; set; }
// Reminder Settings
public int ReminderMinutes { get; set; } = 15; // Default: 15 minutes before
// Color for calendar display
public string EventColor { get; set; } = "primary"; // primary, success, info, warning, danger
// Schedule
public DateTime StartDate { get; set; }
public string? StartTime { get; set; } // e.g., "08:00 AM"
public DateTime EndDate { get; set; }
public string? EndTime { get; set; } // e.g., "08:30 AM"
public bool AllDay { get; set; } = false;
}
}
// ===================================================================================
// SECTION 2: DTOs (Api.SeaHavenIndustries/DTOs/Event_DTO.cs)
// ===================================================================================
namespace Api.SeaHavenIndustries.DTOs
{
// DTO for GET responses
public class Event_DTO
{
public int Id { get; set; }
public string Title { get; set; } = null!;
public string? Description { get; set; }
public string? Location { get; set; }
public int? RecordID { get; set; }
public string? RecordName { get; set; }
public string? RecordTargetType { get; set; }
public int ReminderMinutes { get; set; }
public string EventColor { get; set; } = null!;
public DateTime StartDate { get; set; }
public string? StartTime { get; set; }
public DateTime EndDate { get; set; }
public string? EndTime { get; set; }
public bool AllDay { get; set; }
public DateTime? CreatedDate { get; set; }
public string? createdby { get; set; }
}
// DTO for POST/PUT requests (Create/Edit)
public class EditEvent_DTO
{
public int? Id { get; set; } // Null for create, populated for edit
[Required(ErrorMessage = "Please enter a title")]
[StringLength(100, ErrorMessage = "Title cannot be longer than 100 characters")]
public string Title { get; set; } = null!;
public string? Description { get; set; }
[StringLength(150, ErrorMessage = "Location cannot be longer than 150 characters")]
public string? Location { get; set; }
public int? RecordID { get; set; }
public string? RecordName { get; set; }
public string? RecordTargetType { get; set; }
[Required(ErrorMessage = "The Remind Attendees field is required")]
public int ReminderMinutes { get; set; } = 15;
[Required(ErrorMessage = "The Select a Calendar Color field is required")]
public string EventColor { get; set; } = "primary";
[Required(ErrorMessage = "Please select a start date")]
public DateTime StartDate { get; set; }
[Required(ErrorMessage = "Please select a start time")]
public string StartTime { get; set; } = null!;
[Required(ErrorMessage = "Please select an end date")]
public DateTime EndDate { get; set; }
[Required(ErrorMessage = "Please select an end time")]
public string EndTime { get; set; } = null!;
public bool AllDay { get; set; } = false;
}
}
// ===================================================================================
// SECTION 3: CONTROLLER (Api.SeaHavenIndustries/Controllers/CalendarController.cs)
// ===================================================================================
namespace Api.SeaHavenIndustries.Controllers
{
//[Authorize] // Uncomment when authentication is needed
[ApiController]
[Route("api/Calendar")]
public class CalendarController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly ApplicationDbContext _db;
public CalendarController(UserManager<ApplicationUser> userManager, ApplicationDbContext db)
{
_userManager = userManager;
_db = db;
}
// ===================================================================================
// CREATE EVENT
// ===================================================================================
[HttpPost]
[Route("AddEvent")]
public async Task<IActionResult> AddEvent(EditEvent_DTO eventDto)
{
try
{
var model = new Events
{
Title = eventDto.Title,
Description = eventDto.Description,
Location = eventDto.Location,
RecordID = eventDto.RecordID,
RecordName = eventDto.RecordName,
RecordTargetType = eventDto.RecordTargetType,
ReminderMinutes = eventDto.ReminderMinutes,
EventColor = eventDto.EventColor,
StartDate = eventDto.StartDate,
StartTime = eventDto.StartTime,
EndDate = eventDto.EndDate,
EndTime = eventDto.EndTime,
AllDay = eventDto.AllDay,
CreatedDate = DateTime.Now,
IsDeleted = false
};
_db.Events.Add(model);
await _db.SaveChangesAsync();
return Ok(new DataResponse { Message = "Event Created Successfully", Status = "200" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
// ===================================================================================
// UPDATE EVENT
// ===================================================================================
[HttpPost]
[Route("EditEvent")]
public async Task<IActionResult> EditEvent(EditEvent_DTO eventDto)
{
try
{
if (!eventDto.Id.HasValue)
{
return BadRequest(new Response { Status = "Error", Message = "Event ID is required for editing" });
}
var model = await _db.Events.FindAsync(eventDto.Id.Value);
if (model == null || model.IsDeleted == true)
{
return NotFound(new Response { Status = "Error", Message = "Event not found" });
}
model.Title = eventDto.Title;
model.Description = eventDto.Description;
model.Location = eventDto.Location;
model.RecordID = eventDto.RecordID;
model.RecordName = eventDto.RecordName;
model.RecordTargetType = eventDto.RecordTargetType;
model.ReminderMinutes = eventDto.ReminderMinutes;
model.EventColor = eventDto.EventColor;
model.StartDate = eventDto.StartDate;
model.StartTime = eventDto.StartTime;
model.EndDate = eventDto.EndDate;
model.EndTime = eventDto.EndTime;
model.AllDay = eventDto.AllDay;
model.LastModificationTime = DateTime.Now;
_db.Events.Update(model);
await _db.SaveChangesAsync();
return Ok(new DataResponse { Message = "Event Updated Successfully", Status = "200" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
// ===================================================================================
// GET EVENT BY ID
// ===================================================================================
[HttpGet]
[Route("GetEventById/{id}")]
public async Task<IActionResult> GetEventById(int id)
{
try
{
var model = await _db.Events
.Where(e => e.Id == id && e.IsDeleted != true)
.FirstOrDefaultAsync();
if (model == null)
{
return NotFound(new Response { Status = "Error", Message = "Event not found" });
}
var dto = new Event_DTO
{
Id = model.Id,
Title = model.Title,
Description = model.Description,
Location = model.Location,
RecordID = model.RecordID,
RecordName = model.RecordName,
RecordTargetType = model.RecordTargetType,
ReminderMinutes = model.ReminderMinutes,
EventColor = model.EventColor,
StartDate = model.StartDate,
StartTime = model.StartTime,
EndDate = model.EndDate,
EndTime = model.EndTime,
AllDay = model.AllDay,
CreatedDate = model.CreatedDate,
createdby = model.createdby
};
return Ok(new DataResponse { Message = "Success", Status = "200", Data = dto });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
// ===================================================================================
// DELETE EVENT
// ===================================================================================
[HttpDelete]
[Route("DeleteEvent/{id}")]
public async Task<IActionResult> DeleteEvent(int id)
{
try
{
var model = await _db.Events.FindAsync(id);
if (model == null)
{
return NotFound(new Response { Status = "Error", Message = "Event not found" });
}
// Soft delete
model.IsDeleted = true;
model.DeletionTime = DateTime.Now;
_db.Events.Update(model);
await _db.SaveChangesAsync();
return Ok(new DataResponse { Message = "Event Deleted Successfully", Status = "200" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
// ===================================================================================
// GET EVENTS LIST (WITH PAGINATION AND FILTERS)
// ===================================================================================
[HttpPost]
[Route("GetEventList")]
public IActionResult GetEventList(Pagination_DTO dto)
{
try
{
var query = _db.Events.Where(e => e.IsDeleted != true);
// Apply search filter if provided
if (!string.IsNullOrWhiteSpace(dto.Search))
{
query = query.Where(e =>
e.Title.Contains(dto.Search) ||
(e.Description != null && e.Description.Contains(dto.Search)) ||
(e.Location != null && e.Location.Contains(dto.Search))
);
}
// IMPORTANT: Calculate totalCount AFTER applying filters
var totalCount = query.Count();
// Apply pagination
var events = query
.OrderByDescending(e => e.StartDate)
.ThenByDescending(e => e.CreatedDate)
.Skip((dto.PageNumber - 1) * dto.PageSize)
.Take(dto.PageSize)
.Select(e => new Event_DTO
{
Id = e.Id,
Title = e.Title,
Description = e.Description,
Location = e.Location,
RecordID = e.RecordID,
RecordName = e.RecordName,
RecordTargetType = e.RecordTargetType,
ReminderMinutes = e.ReminderMinutes,
EventColor = e.EventColor,
StartDate = e.StartDate,
StartTime = e.StartTime,
EndDate = e.EndDate,
EndTime = e.EndTime,
AllDay = e.AllDay,
CreatedDate = e.CreatedDate,
createdby = e.createdby
})
.ToList();
return Ok(new DataResponse
{
Message = "Success",
Status = "200",
Data = new { rows = events, totalCount }
});
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
// ===================================================================================
// GET EVENTS FOR CALENDAR (Date Range Filter)
// ===================================================================================
[HttpGet]
[Route("GetEventsForCalendar")]
public IActionResult GetEventsForCalendar([FromQuery] DateTime? start, [FromQuery] DateTime? end)
{
try
{
var query = _db.Events.Where(e => e.IsDeleted != true);
// Filter by date range if provided
if (start.HasValue && end.HasValue)
{
query = query.Where(e =>
(e.StartDate >= start.Value && e.StartDate <= end.Value) ||
(e.EndDate >= start.Value && e.EndDate <= end.Value) ||
(e.StartDate <= start.Value && e.EndDate >= end.Value)
);
}
var events = query
.Select(e => new Event_DTO
{
Id = e.Id,
Title = e.Title,
Description = e.Description,
Location = e.Location,
RecordID = e.RecordID,
RecordName = e.RecordName,
RecordTargetType = e.RecordTargetType,
ReminderMinutes = e.ReminderMinutes,
EventColor = e.EventColor,
StartDate = e.StartDate,
StartTime = e.StartTime,
EndDate = e.EndDate,
EndTime = e.EndTime,
AllDay = e.AllDay,
CreatedDate = e.CreatedDate,
createdby = e.createdby
})
.ToList();
return Ok(new DataResponse { Message = "Success", Status = "200", Data = events });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
}
}
/*
===================================================================================
SECTION 4: DATABASE SETUP
===================================================================================
STEP 1: Add Events DbSet to ApplicationDbContext.cs
-------------------------------------------------------
Add this line in the ApplicationDbContext class (around line 48):
public DbSet<Events> Events { get; set; }
STEP 2: Create Database Migration
-------------------------------------------------------
Run these commands in the terminal from the Api.SeaHavenIndustries project directory:
dotnet ef migrations add AddEventsTable --project ../Data.SeaHavenIndustries
dotnet ef database update
STEP 3: SQL Script for Manual Table Creation (Alternative to migrations)
-------------------------------------------------------
*/
-- SQL SCRIPT TO CREATE EVENTS TABLE
/*
CREATE TABLE Events (
Id INT PRIMARY KEY IDENTITY(1,1),
Title NVARCHAR(100) NOT NULL,
Description NVARCHAR(MAX) NULL,
Location NVARCHAR(150) NULL,
RecordID INT NULL,
RecordName NVARCHAR(255) NULL,
RecordTargetType NVARCHAR(50) NULL,
ReminderMinutes INT NOT NULL DEFAULT 15,
EventColor NVARCHAR(20) NOT NULL DEFAULT 'primary',
StartDate DATETIME NOT NULL,
StartTime NVARCHAR(10) NULL,
EndDate DATETIME NOT NULL,
EndTime NVARCHAR(10) NULL,
AllDay BIT NOT NULL DEFAULT 0,
-- Audit fields
IsDeleted BIT NULL,
createdby NVARCHAR(450) NULL,
DeleterUserId NVARCHAR(450) NULL,
DeletionTime DATETIME NULL,
CreatedDate DATETIME NULL,
LastModificationTime DATETIME NULL,
LastModifierUserId INT NULL
);
-- Insert sample data
INSERT INTO Events (Title, Description, Location, StartDate, StartTime, EndDate, EndTime, AllDay, EventColor, ReminderMinutes, CreatedDate, IsDeleted)
VALUES
('Team Meeting', 'Weekly team sync', 'Conference Room A', '2026-04-01', '10:00 AM', '2026-04-01', '11:00 AM', 0, 'primary', 15, GETDATE(), 0),
('Client Presentation', 'Q1 Results Review with ABC Corp', 'Virtual - Zoom', '2026-04-03', '02:00 PM', '2026-04-03', '03:30 PM', 0, 'success', 30, GETDATE(), 0),
('Project Deadline', 'Final delivery for Project Phoenix', NULL, '2026-04-05', NULL, '2026-04-05', NULL, 1, 'danger', 1440, GETDATE(), 0),
('Training Session', 'New system training for all employees', 'Main Office', '2026-04-08', '09:00 AM', '2026-04-08', '12:00 PM', 0, 'info', 60, GETDATE(), 0),
('Equipment Maintenance', 'Scheduled maintenance for all assets', 'Warehouse', '2026-04-10', '08:00 AM', '2026-04-10', '05:00 PM', 0, 'warning', 120, GETDATE(), 0);
*/
/*
===================================================================================
SECTION 5: IMPLEMENTATION CHECKLIST
===================================================================================
BACKEND SETUP:
--------------
1. ✓ Create Events.cs in Data.SeaHavenIndustries/Models/
2. ✓ Create Event_DTO.cs in Api.SeaHavenIndustries/DTOs/
3. ✓ Create EditEvent_DTO.cs in Api.SeaHavenIndustries/DTOs/
4. ✓ Create CalendarController.cs in Api.SeaHavenIndustries/Controllers/
5. ☐ Add DbSet<Events> to ApplicationDbContext.cs
6. ☐ Run migrations (or execute SQL script)
7. ☐ Test all endpoints using Postman/Swagger
FRONTEND SETUP (Next Step):
---------------------------
1. ☐ Create src/pages/calendar/CalendarPage.js (List view with FullCalendar)
2. ☐ Create src/pages/calendar/EventFormPage.js (Create/Edit form)
3. ☐ Create src/pages/calendar/api.js (API calls)
4. ☐ Add routes to App.js
5. ☐ Install FullCalendar: npm install @fullcalendar/react @fullcalendar/daygrid @fullcalendar/timegrid @fullcalendar/interaction
API ENDPOINTS SUMMARY:
---------------------
POST /api/Calendar/AddEvent - Create new event
POST /api/Calendar/EditEvent - Update existing event
GET /api/Calendar/GetEventById/{id} - Get single event by ID
DELETE /api/Calendar/DeleteEvent/{id} - Delete event (soft delete)
POST /api/Calendar/GetEventList - Get paginated list with search
GET /api/Calendar/GetEventsForCalendar - Get events for calendar view (with date range)
KEY FEATURES IMPLEMENTED:
-------------------------
✓ CRUD operations (Create, Read, Update, Delete)
✓ Pagination with search filter
✓ Date range filtering for calendar view
✓ Soft delete (IsDeleted flag)
✓ Related record linking (RecordID, RecordName, RecordTargetType)
✓ Color coding for calendar display
✓ All-day event support
✓ Reminder settings
✓ Proper error handling
✓ Consistent with existing controller patterns (AssetController)
✓ Fixed pagination bug (totalCount calculated AFTER filters)
NOTES:
------
- EventColor values: 'primary', 'success', 'info', 'warning', 'danger'
- ReminderMinutes: 0=Never, 5, 10, 15, 30, 60, 120, 240, 360, 720, 1440 (1 day)
- StartTime/EndTime format: "08:00 AM", "02:30 PM", etc.
- AllDay: When true, time fields can be null/ignored
- RecordTargetType: Can be "Account", "Contact", "WorkOrder", "FollowUp", etc.
===================================================================================
END OF FILE
===================================================================================
*/

View file

@ -46,6 +46,14 @@ namespace Data.SeaHavenIndustries
public DbSet<FollowUps> FollowUps { get; set; }
public DbSet<PMSchedules> PMSchedules { get; set; }
public DbSet<ContactDetails> ContactDetails { get; set; }
// Employee related DbSets
public DbSet<EmployeeAddress> EmployeeAddresses { get; set; }
public DbSet<EmployeePhone> EmployeePhones { get; set; }
public DbSet<EmployeeEmail> EmployeeEmails { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<JobTitle> JobTitles { get; set; }
public DbSet<Region> Regions { get; set; }
}
public class ApplicationUser : IdentityUser