Add LocationController with full CRUD and extended model

- Extend Locations model with Address, City, State, ZipCode, Phone, Contact, ContactEmail, Status, AccountId
- Update Location DTOs to match new fields
- Create LocationController with paged list, get by ID, create, update, delete
- Support both REST-style routes (/api/Location/{id}) and legacy query routes (/api/Location/DeleteLocation?id=)
- Add migration for new Location columns
This commit is contained in:
Adam Moussa 2026-04-16 17:09:59 -04:00
parent 51b52a7fb5
commit 534f97d6f1
7 changed files with 2560 additions and 3 deletions

View file

@ -0,0 +1,184 @@
using Api.SeaHavenIndustries.DTOs;
using Data.SeaHavenIndustries;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace Api.SeaHavenIndustries.Controllers
{
[Authorize]
[ApiController]
[Route("api/[controller]")]
public class LocationController : Controller
{
private readonly ApplicationDbContext _db;
public LocationController(ApplicationDbContext db)
{
_db = db;
}
[HttpGet("GetLocationList")]
public IActionResult GetLocationList(string? search = "", int page = 1, int pageSize = 10)
{
var query = _db.Locations.AsQueryable();
if (!string.IsNullOrWhiteSpace(search))
{
var s = search.ToLower();
query = query.Where(l =>
(l.Name ?? "").ToLower().Contains(s) ||
(l.Title ?? "").ToLower().Contains(s) ||
(l.Address ?? "").ToLower().Contains(s) ||
(l.City ?? "").ToLower().Contains(s));
}
var totalCount = query.Count();
var data = query
.OrderByDescending(l => l.Id)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(l => new
{
l.Id,
l.Name,
l.Title,
l.Address,
l.City,
l.State,
l.ZipCode,
l.Phone,
l.Contact,
l.ContactEmail,
l.Status,
l.AccountId
})
.ToList();
var viewModel = new Pagination_DTO
{
Data = data,
PageNumber = page,
PageSize = pageSize,
TotalCount = totalCount,
TotalPages = (int)Math.Ceiling(totalCount / (double)pageSize)
};
return Ok(viewModel);
}
[HttpGet("{id}")]
public async Task<IActionResult> GetLocationById(int id)
{
var location = await _db.Locations
.Where(l => l.Id == id)
.Select(l => new
{
l.Id,
l.Name,
l.Title,
l.Address,
l.City,
l.State,
l.ZipCode,
l.Phone,
l.Contact,
l.ContactEmail,
l.Status,
l.AccountId
})
.FirstOrDefaultAsync();
if (location == null)
return NotFound(new Response { Status = "Error", Message = "Location not found" });
return Ok(location);
}
[HttpPost]
public async Task<IActionResult> AddLocation([FromBody] Location_DTO model)
{
try
{
var location = new Locations
{
Name = model.Name,
Title = model.Title,
Address = model.Address,
City = model.City,
State = model.State,
ZipCode = model.ZipCode,
Phone = model.Phone,
Contact = model.Contact,
ContactEmail = model.ContactEmail,
Status = model.Status,
AccountId = model.AccountId
};
_db.Locations.Add(location);
await _db.SaveChangesAsync();
return Ok(new DataResponse { Message = "Location Created Successfully", Status = "200" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
[HttpPut("{id}")]
public async Task<IActionResult> EditLocation(int id, [FromBody] EditLocation_DTO model)
{
try
{
var exist = await _db.Locations.Where(l => l.Id == id).FirstOrDefaultAsync();
if (exist == null)
return BadRequest(new Response { Status = "Error", Message = "Location not found" });
exist.Name = model.Name;
exist.Title = model.Title;
exist.Address = model.Address;
exist.City = model.City;
exist.State = model.State;
exist.ZipCode = model.ZipCode;
exist.Phone = model.Phone;
exist.Contact = model.Contact;
exist.ContactEmail = model.ContactEmail;
exist.Status = model.Status;
exist.AccountId = model.AccountId;
await _db.SaveChangesAsync();
return Ok(new DataResponse { Message = "Location Updated Successfully", Status = "200" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteLocation(int id)
{
try
{
var exist = await _db.Locations.Where(l => l.Id == id).FirstOrDefaultAsync();
if (exist == null)
return BadRequest(new Response { Status = "Error", Message = "Location not found" });
_db.Locations.Remove(exist);
await _db.SaveChangesAsync();
return Ok(new DataResponse { Message = "Location Deleted Successfully", Status = "200" });
}
catch (Exception ex)
{
return BadRequest(new Response { Status = "Error", Message = ex.Message });
}
}
[HttpGet("DeleteLocation")]
public async Task<IActionResult> DeleteLocationByQuery(int id)
{
return await DeleteLocation(id);
}
}
}

View file

@ -1,9 +1,18 @@
namespace Api.SeaHavenIndustries.DTOs
namespace Api.SeaHavenIndustries.DTOs
{
public class EditLocation_DTO
{
public int Id { get; set; }
public string? Title { get; set; }
public string? Name { get; set; }
public string? Address { get; set; }
public string? City { get; set; }
public string? State { get; set; }
public string? ZipCode { get; set; }
public string? Phone { get; set; }
public string? Contact { get; set; }
public string? ContactEmail { get; set; }
public string? Status { get; set; }
public int? AccountId { get; set; }
}
}

View file

@ -1,8 +1,17 @@
namespace Api.SeaHavenIndustries.DTOs
namespace Api.SeaHavenIndustries.DTOs
{
public class Location_DTO
{
public string? Title { get; set; }
public string? Name { get; set; }
public string? Address { get; set; }
public string? City { get; set; }
public string? State { get; set; }
public string? ZipCode { get; set; }
public string? Phone { get; set; }
public string? Contact { get; set; }
public string? ContactEmail { get; set; }
public string? Status { get; set; }
public int? AccountId { get; set; }
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,129 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Data.SeaHavenIndustries.Migrations
{
/// <inheritdoc />
public partial class AddLocationFields : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "AccountId",
table: "Locations",
type: "int",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Address",
table: "Locations",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "City",
table: "Locations",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Contact",
table: "Locations",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "ContactEmail",
table: "Locations",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Phone",
table: "Locations",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "State",
table: "Locations",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Status",
table: "Locations",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "ZipCode",
table: "Locations",
type: "nvarchar(max)",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_Locations_AccountId",
table: "Locations",
column: "AccountId");
migrationBuilder.AddForeignKey(
name: "FK_Locations_Accounts_AccountId",
table: "Locations",
column: "AccountId",
principalTable: "Accounts",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Locations_Accounts_AccountId",
table: "Locations");
migrationBuilder.DropIndex(
name: "IX_Locations_AccountId",
table: "Locations");
migrationBuilder.DropColumn(
name: "AccountId",
table: "Locations");
migrationBuilder.DropColumn(
name: "Address",
table: "Locations");
migrationBuilder.DropColumn(
name: "City",
table: "Locations");
migrationBuilder.DropColumn(
name: "Contact",
table: "Locations");
migrationBuilder.DropColumn(
name: "ContactEmail",
table: "Locations");
migrationBuilder.DropColumn(
name: "Phone",
table: "Locations");
migrationBuilder.DropColumn(
name: "State",
table: "Locations");
migrationBuilder.DropColumn(
name: "Status",
table: "Locations");
migrationBuilder.DropColumn(
name: "ZipCode",
table: "Locations");
}
}
}

View file

@ -979,6 +979,21 @@ namespace Data.SeaHavenIndustries.Migrations
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int?>("AccountId")
.HasColumnType("int");
b.Property<string>("Address")
.HasColumnType("nvarchar(max)");
b.Property<string>("City")
.HasColumnType("nvarchar(max)");
b.Property<string>("Contact")
.HasColumnType("nvarchar(max)");
b.Property<string>("ContactEmail")
.HasColumnType("nvarchar(max)");
b.Property<DateTime?>("CreatedDate")
.HasColumnType("datetime2");
@ -1006,14 +1021,28 @@ namespace Data.SeaHavenIndustries.Migrations
b.Property<string>("Name")
.HasColumnType("nvarchar(max)");
b.Property<string>("Phone")
.HasColumnType("nvarchar(max)");
b.Property<string>("State")
.HasColumnType("nvarchar(max)");
b.Property<string>("Status")
.HasColumnType("nvarchar(max)");
b.Property<string>("Title")
.HasColumnType("nvarchar(max)");
b.Property<string>("ZipCode")
.HasColumnType("nvarchar(max)");
b.Property<string>("createdby")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("AccountId");
b.ToTable("Locations");
});
@ -1863,6 +1892,16 @@ namespace Data.SeaHavenIndustries.Migrations
b.Navigation("WorkOrder");
});
modelBuilder.Entity("Data.SeaHavenIndustries.Locations", b =>
{
b.HasOne("Data.SeaHavenIndustries.Accounts", "Account")
.WithMany()
.HasForeignKey("AccountId")
.OnDelete(DeleteBehavior.Restrict);
b.Navigation("Account");
});
modelBuilder.Entity("Data.SeaHavenIndustries.PMSchedules", b =>
{
b.HasOne("Data.SeaHavenIndustries.Assets", "Asset")

View file

@ -8,10 +8,20 @@ namespace Data.SeaHavenIndustries
{
public string? Title { get; set; }
public string? Name { get; set; }
public string? Address { get; set; }
public string? City { get; set; }
public string? State { get; set; }
public string? ZipCode { get; set; }
public string? Phone { get; set; }
public string? Contact { get; set; }
public string? ContactEmail { get; set; }
public string? Status { get; set; }
public string? Latitude { get; set; }
public string? Longitude { get; set; }
public int? AccountId { get; set; }
[ForeignKey("AccountId")]
public Accounts? Account { get; set; }
public ICollection<Template>? Templates { get; set; }
public ICollection<WorkOrder>? workOrders { get; set; }
}
}