Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .vs/FinalProject/DesignTimeBuild/.dtbcache.v2
Binary file not shown.
Binary file modified .vs/FinalProject/v17/.suo
Binary file not shown.
395 changes: 23 additions & 372 deletions .vs/FinalProject/v17/DocumentLayout.backup.json

Large diffs are not rendered by default.

392 changes: 26 additions & 366 deletions .vs/FinalProject/v17/DocumentLayout.json

Large diffs are not rendered by default.

48 changes: 33 additions & 15 deletions FinalProject/Controllers/AccountController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public async Task<ActionResult> Register(RegisterViewModel rvm)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
return View(rvm);
}

// Check if user is at least 18 years old
Expand All @@ -61,6 +61,7 @@ public async Task<ActionResult> Register(RegisterViewModel rvm)
return View(rvm);
}

// Create the user
AppUser newUser = new AppUser
{
UserName = rvm.Email,
Expand All @@ -72,27 +73,44 @@ public async Task<ActionResult> Register(RegisterViewModel rvm)
Birthday = rvm.Birthday
};

AddUserModel aum = new AddUserModel()
{
User = newUser,
Password = rvm.Password,
RoleName = rvm.Role
};

IdentityResult result = await AddUser.AddUserWithRoleAsync(aum, _userManager, _context);
IdentityResult userResult = await _userManager.CreateAsync(newUser, rvm.Password);

if (result.Succeeded)
if (!userResult.Succeeded)
{
await _signInManager.SignInAsync(newUser, isPersistent: false);
return RedirectToAction("Index", "Home");
foreach (var error in userResult.Errors)
{
ModelState.AddModelError("", error.Description);
}
return View(rvm);
}

foreach (IdentityError error in result.Errors)
// Check and assign role
if (!string.IsNullOrEmpty(rvm.Role))
{
ModelState.AddModelError("", error.Description);
var roleExists = await _context.Roles.AnyAsync(r => r.Name == rvm.Role);
if (!roleExists)
{
_context.Roles.Add(new IdentityRole { Name = rvm.Role });
await _context.SaveChangesAsync();
}

IdentityResult roleResult = await _userManager.AddToRoleAsync(newUser, rvm.Role);
if (!roleResult.Succeeded)
{
// If role assignment fails, delete the user to maintain consistency
await _userManager.DeleteAsync(newUser);

foreach (var error in roleResult.Errors)
{
ModelState.AddModelError("", error.Description);
}
return View(rvm);
}
}

return View(rvm);
// Sign in the user
await _signInManager.SignInAsync(newUser, isPersistent: false);
return RedirectToAction("Index", "Home");
}

[AllowAnonymous]
Expand Down
138 changes: 83 additions & 55 deletions FinalProject/Controllers/HomeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,14 @@ public async Task<IActionResult> Index()
.Include(p => p.Category)
.Include(p => p.Reviews)
.Include(p => p.Host)
.Where(p => p.PropertyStatus && p.IsActive)
.Where(p => p.PropertyStatus && p.IsActive)
.ToListAsync();

// Get total count of only approved and active properties
ViewBag.TotalCount = await _context.Properties
.CountAsync(p => p.PropertyStatus && p.IsActive);

ViewBag.FilteredCount = properties.Count;

// Get all categories for the search filters
ViewBag.Categories = await _context.Categories.ToListAsync();
Expand Down Expand Up @@ -56,89 +60,89 @@ public async Task<IActionResult> Details(int? id)
// GET: /Home/
public async Task<IActionResult> Search(
string location = null,
string propertyNumber = null,
string state = null,
DateTime? checkIn = null,
DateTime? checkOut = null,
int? guests = null,
int? categoryId = null,
int[] categories = null,
decimal? minPrice = null,
decimal? maxPrice = null,
int? minBedrooms = null,
int? maxBedrooms = null,
int? minBathrooms = null,
int? maxBathrooms = null,
decimal? minRating = null,
decimal? maxRating = null,
bool? petsAllowed = null,
bool? freeParking = null)
{
var query = _context.Properties
.Include(p => p.Category)
.Include(p => p.Reviews)
.Include(p => p.Host)
.Where(p => p.PropertyStatus);
.Include(p => p.Reservations)
.Include(p => p.UnavailableDates)
.Where(p => p.PropertyStatus && p.IsActive); // Only show active and approved properties

// Apply filters one by one
if (!string.IsNullOrEmpty(location))
{
query = ApplyLocationFilter(query, location);
}

// Property Number filter
if (!string.IsNullOrEmpty(propertyNumber))
{
query = query.Where(p => p.PropertyNumber.ToString().Contains(propertyNumber));
}

// Apply search filters function
if (!string.IsNullOrEmpty(location))
// State filter
if (!string.IsNullOrEmpty(state))
{
var searchTerms = location.ToLower()
.Split(',')
.SelectMany(term => term.Trim().Split(' '))
.Where(term => !string.IsNullOrWhiteSpace(term))
.ToArray();

// If single search term, use original logic
if (searchTerms.Length == 1)
{
var singleTerm = searchTerms[0];
query = query.Where(p =>
p.City.ToLower().Contains(singleTerm) ||
p.State.ToLower().Contains(singleTerm) ||
p.Street.ToLower().Contains(singleTerm) ||
p.Zip.Contains(singleTerm)
);
}
// If multiple terms, check each part separately
else
{
query = query.Where(p =>
searchTerms.All(term =>
p.Street.ToLower().Contains(term) ||
p.City.ToLower().Contains(term) ||
p.State.ToLower().Contains(term) ||
p.Zip.Contains(term)
)
);
}
query = query.Where(p => p.State.ToLower().Contains(state.ToLower()));
}

if (guests.HasValue)
// Multiple Categories filter
if (categories != null && categories.Length > 0)
{
query = query.Where(p => p.GuestsAllowed >= guests.Value);
query = query.Where(p => categories.Contains(p.CategoryID));
}

if (categoryId.HasValue)
if (guests.HasValue)
{
query = query.Where(p => p.Category.CategoryID == categoryId.Value);
query = query.Where(p => p.GuestsAllowed >= guests.Value);
}

if (minPrice.HasValue)
{
query = query.Where(p => p.WeekdayPrice >= minPrice.Value);
query = query.Where(p => p.WeekdayPrice >= minPrice.Value || p.WeekendPrice >= minPrice.Value);
}

if (maxPrice.HasValue)
{
query = query.Where(p => p.WeekdayPrice <= maxPrice.Value);
query = query.Where(p => p.WeekdayPrice <= maxPrice.Value && p.WeekendPrice <= maxPrice.Value);
}

// Bedroom range
if (minBedrooms.HasValue)
{
query = query.Where(p => p.Bedrooms >= minBedrooms.Value);
}
if (maxBedrooms.HasValue)
{
query = query.Where(p => p.Bedrooms <= maxBedrooms.Value);
}

// Bathroom range
if (minBathrooms.HasValue)
{
query = query.Where(p => p.Bathrooms >= minBathrooms.Value);
}
if (maxBathrooms.HasValue)
{
query = query.Where(p => p.Bathrooms <= maxBathrooms.Value);
}

if (petsAllowed.HasValue)
{
Expand All @@ -150,38 +154,62 @@ public async Task<IActionResult> Search(
query = query.Where(p => p.FreeParking == freeParking.Value);
}

// Check availability if dates are provided
// Handle date availability
if (checkIn.HasValue && checkOut.HasValue)
{
query = query.Where(p => !p.Reservations.Any(r =>
r.ReservationStatus && // Only consider active reservations
((checkIn >= r.CheckIn && checkIn < r.CheckOut) || // Check-in during existing reservation
(checkOut > r.CheckIn && checkOut <= r.CheckOut) || // Check-out during existing reservation
(checkIn <= r.CheckIn && checkOut >= r.CheckOut)))); // Existing reservation within requested dates
query = query.Where(p =>
// No overlapping reservations
!p.Reservations.Any(r =>
r.ReservationStatus == true &&
(
(checkIn >= r.CheckIn && checkIn < r.CheckOut) ||
(checkOut > r.CheckIn && checkOut <= r.CheckOut) ||
(checkIn <= r.CheckIn && checkOut >= r.CheckOut)
)
) &&
// No unavailable dates
!p.UnavailableDates.Any(ud =>
ud.Date >= checkIn && ud.Date < checkOut
)
);
}

// Apply rating filter if specified
if (minRating.HasValue)
// Apply rating filter with range
if (minRating.HasValue || maxRating.HasValue)
{
query = query.Where(p =>
p.Reviews.Any() &&
p.Reviews.Where(r => r.DisputeStatus != DisputeStatus.ValidDispute)
.Average(r => (decimal)r.Rating) >= minRating.Value);
(!minRating.HasValue || p.Reviews.Where(r => r.DisputeStatus != DisputeStatus.ValidDispute)
.Average(r => (decimal)r.Rating) >= minRating.Value) &&
(!maxRating.HasValue || p.Reviews.Where(r => r.DisputeStatus != DisputeStatus.ValidDispute)
.Average(r => (decimal)r.Rating) <= maxRating.Value));
}

var properties = await query.ToListAsync();



// Populate ViewBag data for the view
ViewBag.TotalCount = await _context.Properties.CountAsync();
// Set ViewBag data for the view
ViewBag.TotalCount = await _context.Properties.CountAsync(p => p.PropertyStatus && p.IsActive);
ViewBag.FilteredCount = properties.Count;
ViewBag.Categories = await _context.Categories.ToListAsync();

// Return the search view with results
return View("Index", properties);
}

private IQueryable<Property> ApplyLocationFilter(IQueryable<Property> query, string location)
{
var searchTerms = location.ToLower()
.Split(new[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(term => term.Trim())
.Where(term => !string.IsNullOrWhiteSpace(term))
.ToArray();

return query.Where(p => searchTerms.All(term =>
p.Street.ToLower().Contains(term) ||
p.City.ToLower().Contains(term) ||
p.State.ToLower().Contains(term) ||
p.Zip.Contains(term)));
}

public IActionResult Privacy()
{
return View();
Expand Down
Loading